add more methods to file system interface
This commit is contained in:
parent
88b638fbd6
commit
b9d17c9bc7
|
@ -46,6 +46,18 @@ namespace MediaBrowser.Api
|
|||
public bool IncludeHidden { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Environment/NetworkShares", "GET")]
|
||||
[Api(Description = "Gets shares from a network device")]
|
||||
public class GetNetworkShares : IReturn<List<FileSystemEntryInfo>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the path.
|
||||
/// </summary>
|
||||
/// <value>The path.</value>
|
||||
[ApiMember(Name = "Path", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string Path { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetDrives
|
||||
/// </summary>
|
||||
|
@ -64,11 +76,25 @@ namespace MediaBrowser.Api
|
|||
{
|
||||
}
|
||||
|
||||
[Route("/Environment/ParentPath", "GET")]
|
||||
[Api(Description = "Gets the parent path of a given path")]
|
||||
public class GetParentPath : IReturn<string>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the path.
|
||||
/// </summary>
|
||||
/// <value>The path.</value>
|
||||
[ApiMember(Name = "Path", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string Path { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class EnvironmentService
|
||||
/// </summary>
|
||||
public class EnvironmentService : BaseApiService
|
||||
{
|
||||
const char UncSeparator = '\\';
|
||||
|
||||
/// <summary>
|
||||
/// The _network manager
|
||||
/// </summary>
|
||||
|
@ -105,13 +131,9 @@ namespace MediaBrowser.Api
|
|||
throw new ArgumentNullException("Path");
|
||||
}
|
||||
|
||||
// If it's not a drive trim trailing slashes.
|
||||
if (!path.EndsWith(":\\"))
|
||||
{
|
||||
path = path.TrimEnd('\\');
|
||||
}
|
||||
var networkPrefix = UncSeparator.ToString(CultureInfo.InvariantCulture) + UncSeparator.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (path.StartsWith(NetworkPrefix, StringComparison.OrdinalIgnoreCase) && path.LastIndexOf('\\') == 1)
|
||||
if (path.StartsWith(networkPrefix, StringComparison.OrdinalIgnoreCase) && path.LastIndexOf(UncSeparator) == 1)
|
||||
{
|
||||
return ToOptimizedResult(GetNetworkShares(path).OrderBy(i => i.Path).ToList());
|
||||
}
|
||||
|
@ -119,6 +141,15 @@ namespace MediaBrowser.Api
|
|||
return ToOptimizedResult(GetFileSystemEntries(request).OrderBy(i => i.Path).ToList());
|
||||
}
|
||||
|
||||
public object Get(GetNetworkShares request)
|
||||
{
|
||||
var path = request.Path;
|
||||
|
||||
var shares = GetNetworkShares(path).OrderBy(i => i.Path).ToList();
|
||||
|
||||
return ToOptimizedResult(shares);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
|
@ -154,25 +185,13 @@ namespace MediaBrowser.Api
|
|||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetNetworkDevices request)
|
||||
{
|
||||
var result = GetNetworkDevices().OrderBy(i => i.Path).ToList();
|
||||
var result = _networkManager.GetNetworkDevices()
|
||||
.OrderBy(i => i.Path)
|
||||
.ToList();
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the network computers.
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{FileSystemEntryInfo}.</returns>
|
||||
private IEnumerable<FileSystemEntryInfo> GetNetworkDevices()
|
||||
{
|
||||
return _networkManager.GetNetworkDevices().Select(c => new FileSystemEntryInfo
|
||||
{
|
||||
Name = c,
|
||||
Path = NetworkPrefix + c,
|
||||
Type = FileSystemEntryType.NetworkComputer
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
|
@ -223,7 +242,7 @@ namespace MediaBrowser.Api
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
|
@ -236,13 +255,27 @@ namespace MediaBrowser.Api
|
|||
}).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the network prefix.
|
||||
/// </summary>
|
||||
/// <value>The network prefix.</value>
|
||||
private string NetworkPrefix
|
||||
public object Get(GetParentPath request)
|
||||
{
|
||||
get { return Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture) + Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture); }
|
||||
var parent = Path.GetDirectoryName(request.Path);
|
||||
|
||||
if (string.IsNullOrEmpty(parent))
|
||||
{
|
||||
// Check if unc share
|
||||
var index = request.Path.LastIndexOf(UncSeparator);
|
||||
|
||||
if (index != -1 && request.Path.IndexOf(UncSeparator) == 0)
|
||||
{
|
||||
parent = request.Path.Substring(0, index);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(parent.TrimStart(UncSeparator)))
|
||||
{
|
||||
parent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,8 +59,11 @@ namespace MediaBrowser.Api.Images
|
|||
[ApiMember(Name = "AddPlayedIndicator", Description = "Optional. Add a played indicator", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool AddPlayedIndicator { get; set; }
|
||||
|
||||
[ApiMember(Name = "PercentPlayed", Description = "Optional percent to render for the percent played overlay", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public int? PercentPlayed { get; set; }
|
||||
[ApiMember(Name = "PercentPlayed", Description = "Optional percent to render for the percent played overlay", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public double? PercentPlayed { get; set; }
|
||||
|
||||
[ApiMember(Name = "UnplayedCount", Description = "Optional unplayed count overlay to render", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? UnplayedCount { get; set; }
|
||||
|
||||
[ApiMember(Name = "BackgroundColor", Description = "Optional. Apply a background color for transparent images.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string BackgroundColor { get; set; }
|
||||
|
|
|
@ -91,6 +91,7 @@ namespace MediaBrowser.Api.Images
|
|||
OutputFormat = Request.Format,
|
||||
AddPlayedIndicator = Request.AddPlayedIndicator,
|
||||
PercentPlayed = Request.PercentPlayed,
|
||||
UnplayedCount = Request.UnplayedCount,
|
||||
BackgroundColor = Request.BackgroundColor
|
||||
};
|
||||
|
||||
|
|
|
@ -65,17 +65,10 @@ namespace MediaBrowser.Api.Library
|
|||
throw new DirectoryNotFoundException("The path does not exist.");
|
||||
}
|
||||
|
||||
// Strip off trailing slash, but not on drives
|
||||
path = path.TrimEnd(Path.DirectorySeparatorChar);
|
||||
if (path.EndsWith(":", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path += Path.DirectorySeparatorChar;
|
||||
}
|
||||
|
||||
var rootFolderPath = user != null ? user.RootFolderPath : appPaths.DefaultUserViewsPath;
|
||||
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
||||
|
||||
ValidateNewMediaPath(fileSystem, rootFolderPath, path, appPaths);
|
||||
ValidateNewMediaPath(fileSystem, rootFolderPath, path);
|
||||
|
||||
var shortcutFilename = Path.GetFileNameWithoutExtension(path);
|
||||
|
||||
|
@ -96,25 +89,18 @@ namespace MediaBrowser.Api.Library
|
|||
/// <param name="fileSystem">The file system.</param>
|
||||
/// <param name="currentViewRootFolderPath">The current view root folder path.</param>
|
||||
/// <param name="mediaPath">The media path.</param>
|
||||
/// <param name="appPaths">The app paths.</param>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// </exception>
|
||||
private static void ValidateNewMediaPath(IFileSystem fileSystem, string currentViewRootFolderPath, string mediaPath, IServerApplicationPaths appPaths)
|
||||
private static void ValidateNewMediaPath(IFileSystem fileSystem, string currentViewRootFolderPath, string mediaPath)
|
||||
{
|
||||
var duplicate = Directory.EnumerateFiles(appPaths.RootFolderPath, ShortcutFileSearch, SearchOption.AllDirectories)
|
||||
.Select(fileSystem.ResolveShortcut)
|
||||
.FirstOrDefault(p => !IsNewPathValid(mediaPath, p, false));
|
||||
|
||||
if (!string.IsNullOrEmpty(duplicate))
|
||||
{
|
||||
throw new ArgumentException(string.Format("The path cannot be added to the library because {0} already exists.", duplicate));
|
||||
}
|
||||
var pathsInCurrentVIew = Directory.EnumerateFiles(currentViewRootFolderPath, ShortcutFileSearch, SearchOption.AllDirectories)
|
||||
.Select(fileSystem.ResolveShortcut)
|
||||
.ToList();
|
||||
|
||||
// Don't allow duplicate sub-paths within the same user library, or it will result in duplicate items
|
||||
// See comments in IsNewPathValid
|
||||
duplicate = Directory.EnumerateFiles(currentViewRootFolderPath, ShortcutFileSearch, SearchOption.AllDirectories)
|
||||
.Select(fileSystem.ResolveShortcut)
|
||||
.FirstOrDefault(p => !IsNewPathValid(mediaPath, p, true));
|
||||
var duplicate = pathsInCurrentVIew
|
||||
.FirstOrDefault(p => !IsNewPathValid(fileSystem, mediaPath, p));
|
||||
|
||||
if (!string.IsNullOrEmpty(duplicate))
|
||||
{
|
||||
|
@ -122,9 +108,8 @@ namespace MediaBrowser.Api.Library
|
|||
}
|
||||
|
||||
// Make sure the current root folder doesn't already have a shortcut to the same path
|
||||
duplicate = Directory.EnumerateFiles(currentViewRootFolderPath, ShortcutFileSearch, SearchOption.AllDirectories)
|
||||
.Select(fileSystem.ResolveShortcut)
|
||||
.FirstOrDefault(p => mediaPath.Equals(p, StringComparison.OrdinalIgnoreCase));
|
||||
duplicate = pathsInCurrentVIew
|
||||
.FirstOrDefault(p => string.Equals(mediaPath, p, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (!string.IsNullOrEmpty(duplicate))
|
||||
{
|
||||
|
@ -135,30 +120,30 @@ namespace MediaBrowser.Api.Library
|
|||
/// <summary>
|
||||
/// Validates that a new path can be added based on an existing path
|
||||
/// </summary>
|
||||
/// <param name="fileSystem">The file system.</param>
|
||||
/// <param name="newPath">The new path.</param>
|
||||
/// <param name="existingPath">The existing path.</param>
|
||||
/// <param name="enforceSubPathRestriction">if set to <c>true</c> [enforce sub path restriction].</param>
|
||||
/// <returns><c>true</c> if [is new path valid] [the specified new path]; otherwise, <c>false</c>.</returns>
|
||||
private static bool IsNewPathValid(string newPath, string existingPath, bool enforceSubPathRestriction)
|
||||
private static bool IsNewPathValid(IFileSystem fileSystem, string newPath, string existingPath)
|
||||
{
|
||||
// Example: D:\Movies is the existing path
|
||||
// D:\ cannot be added
|
||||
// Neither can D:\Movies\Kids
|
||||
// A D:\Movies duplicate is ok here since that will be caught later
|
||||
|
||||
if (newPath.Equals(existingPath, StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(newPath, existingPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If enforceSubPathRestriction is true, validate the D:\Movies\Kids scenario
|
||||
if (enforceSubPathRestriction && newPath.StartsWith(existingPath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))
|
||||
if (fileSystem.ContainsSubPath(existingPath, newPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate the D:\ scenario
|
||||
if (existingPath.StartsWith(newPath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))
|
||||
if (fileSystem.ContainsSubPath(newPath, existingPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,9 @@ namespace MediaBrowser.Api.LiveTv
|
|||
|
||||
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? Limit { get; set; }
|
||||
|
||||
[ApiMember(Name = "IsRecording", Description = "Optional filter by recordings that are currently active, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool? IsRecording { get; set; }
|
||||
}
|
||||
|
||||
[Route("/LiveTv/Recordings/Groups", "GET")]
|
||||
|
@ -274,7 +277,8 @@ namespace MediaBrowser.Api.LiveTv
|
|||
UserId = request.UserId,
|
||||
GroupId = request.GroupId,
|
||||
StartIndex = request.StartIndex,
|
||||
Limit = request.Limit
|
||||
Limit = request.Limit,
|
||||
IsRecording = request.IsRecording
|
||||
|
||||
}, CancellationToken.None).Result;
|
||||
|
||||
|
|
|
@ -197,10 +197,6 @@ namespace MediaBrowser.Api.Playback
|
|||
{
|
||||
args += string.Format("-map 0:{0}", state.VideoStream.Index);
|
||||
}
|
||||
else if (!state.HasMediaStreams)
|
||||
{
|
||||
args += string.Format("-map 0:{0}", 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
args += "-map -0:v";
|
||||
|
@ -210,10 +206,6 @@ namespace MediaBrowser.Api.Playback
|
|||
{
|
||||
args += string.Format(" -map 0:{0}", state.AudioStream.Index);
|
||||
}
|
||||
else if (!state.HasMediaStreams)
|
||||
{
|
||||
args += string.Format(" -map 0:{0}", 1);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
|
@ -871,7 +863,7 @@ namespace MediaBrowser.Api.Playback
|
|||
RequestedUrl = url
|
||||
};
|
||||
|
||||
BaseItem item;
|
||||
Guid itemId;
|
||||
|
||||
if (string.Equals(request.Type, "Recording", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
|
@ -900,7 +892,7 @@ namespace MediaBrowser.Api.Playback
|
|||
state.IsRemote = true;
|
||||
}
|
||||
|
||||
item = recording;
|
||||
itemId = recording.Id;
|
||||
}
|
||||
else if (string.Equals(request.Type, "Channel", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
|
@ -916,11 +908,11 @@ namespace MediaBrowser.Api.Playback
|
|||
|
||||
state.IsRemote = true;
|
||||
|
||||
item = channel;
|
||||
itemId = channel.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
item = DtoService.GetItemByDtoId(request.Id);
|
||||
var item = DtoService.GetItemByDtoId(request.Id);
|
||||
|
||||
state.MediaPath = item.Path;
|
||||
state.IsRemote = item.LocationType == LocationType.Remote;
|
||||
|
@ -937,13 +929,15 @@ namespace MediaBrowser.Api.Playback
|
|||
? new List<string>()
|
||||
: video.PlayableStreamFileNames.ToList();
|
||||
}
|
||||
|
||||
itemId = item.Id;
|
||||
}
|
||||
|
||||
var videoRequest = request as VideoStreamRequest;
|
||||
|
||||
var mediaStreams = ItemRepository.GetMediaStreams(new MediaStreamQuery
|
||||
{
|
||||
ItemId = item.Id
|
||||
ItemId = itemId
|
||||
|
||||
}).ToList();
|
||||
|
||||
|
|
|
@ -82,6 +82,16 @@ namespace MediaBrowser.Common.Implementations.IO
|
|||
throw new ArgumentNullException("target");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(shortcutPath))
|
||||
{
|
||||
throw new ArgumentNullException("shortcutPath");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(target))
|
||||
{
|
||||
throw new ArgumentNullException("target");
|
||||
}
|
||||
|
||||
File.WriteAllText(shortcutPath, target);
|
||||
}
|
||||
|
||||
|
@ -92,6 +102,11 @@ namespace MediaBrowser.Common.Implementations.IO
|
|||
/// <returns>FileSystemInfo.</returns>
|
||||
public FileSystemInfo GetFileSystemInfo(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
// Take a guess to try and avoid two file system hits, but we'll double-check by calling Exists
|
||||
if (Path.HasExtension(path))
|
||||
{
|
||||
|
@ -172,7 +187,6 @@ namespace MediaBrowser.Common.Implementations.IO
|
|||
/// Gets the creation time UTC.
|
||||
/// </summary>
|
||||
/// <param name="info">The info.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <returns>DateTime.</returns>
|
||||
public DateTime GetLastWriteTimeUtc(FileSystemInfo info)
|
||||
{
|
||||
|
@ -224,6 +238,16 @@ namespace MediaBrowser.Common.Implementations.IO
|
|||
/// <param name="file2">The file2.</param>
|
||||
public void SwapFiles(string file1, string file2)
|
||||
{
|
||||
if (string.IsNullOrEmpty(file1))
|
||||
{
|
||||
throw new ArgumentNullException("file1");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(file2))
|
||||
{
|
||||
throw new ArgumentNullException("file2");
|
||||
}
|
||||
|
||||
var temp1 = Path.GetTempFileName();
|
||||
var temp2 = Path.GetTempFileName();
|
||||
|
||||
|
@ -247,6 +271,11 @@ namespace MediaBrowser.Common.Implementations.IO
|
|||
/// <param name="path">The path.</param>
|
||||
private void RemoveHiddenAttribute(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
var currentFile = new FileInfo(path);
|
||||
|
||||
// This will fail if the file is hidden
|
||||
|
@ -258,127 +287,52 @@ namespace MediaBrowser.Common.Implementations.IO
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsSubPath(string parentPath, string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(parentPath))
|
||||
{
|
||||
throw new ArgumentNullException("parentPath");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
return path.IndexOf(parentPath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) != -1;
|
||||
}
|
||||
|
||||
public bool IsRootPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
var parent = Path.GetDirectoryName(path);
|
||||
|
||||
if (!string.IsNullOrEmpty(parent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public string NormalizePath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
if (path.EndsWith(":\\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
return path.TrimEnd(Path.DirectorySeparatorChar);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adapted from http://stackoverflow.com/questions/309495/windows-shortcut-lnk-parser-in-java
|
||||
/// </summary>
|
||||
internal class WindowsShortcut
|
||||
{
|
||||
public bool IsDirectory { get; private set; }
|
||||
public bool IsLocal { get; private set; }
|
||||
public string ResolvedPath { get; private set; }
|
||||
|
||||
public WindowsShortcut(string file)
|
||||
{
|
||||
ParseLink(File.ReadAllBytes(file), Encoding.UTF8);
|
||||
}
|
||||
|
||||
private static bool isMagicPresent(byte[] link)
|
||||
{
|
||||
|
||||
const int magic = 0x0000004C;
|
||||
const int magic_offset = 0x00;
|
||||
|
||||
return link.Length >= 32 && bytesToDword(link, magic_offset) == magic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gobbles up link data by parsing it and storing info in member fields
|
||||
* @param link all the bytes from the .lnk file
|
||||
*/
|
||||
private void ParseLink(byte[] link, Encoding encoding)
|
||||
{
|
||||
if (!isMagicPresent(link))
|
||||
throw new IOException("Invalid shortcut; magic is missing", 0);
|
||||
|
||||
// get the flags byte
|
||||
byte flags = link[0x14];
|
||||
|
||||
// get the file attributes byte
|
||||
const int file_atts_offset = 0x18;
|
||||
byte file_atts = link[file_atts_offset];
|
||||
byte is_dir_mask = (byte)0x10;
|
||||
if ((file_atts & is_dir_mask) > 0)
|
||||
{
|
||||
IsDirectory = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsDirectory = false;
|
||||
}
|
||||
|
||||
// if the shell settings are present, skip them
|
||||
const int shell_offset = 0x4c;
|
||||
const byte has_shell_mask = (byte)0x01;
|
||||
int shell_len = 0;
|
||||
if ((flags & has_shell_mask) > 0)
|
||||
{
|
||||
// the plus 2 accounts for the length marker itself
|
||||
shell_len = bytesToWord(link, shell_offset) + 2;
|
||||
}
|
||||
|
||||
// get to the file settings
|
||||
int file_start = 0x4c + shell_len;
|
||||
|
||||
const int file_location_info_flag_offset_offset = 0x08;
|
||||
int file_location_info_flag = link[file_start + file_location_info_flag_offset_offset];
|
||||
IsLocal = (file_location_info_flag & 2) == 0;
|
||||
// get the local volume and local system values
|
||||
//final int localVolumeTable_offset_offset = 0x0C;
|
||||
const int basename_offset_offset = 0x10;
|
||||
const int networkVolumeTable_offset_offset = 0x14;
|
||||
const int finalname_offset_offset = 0x18;
|
||||
int finalname_offset = link[file_start + finalname_offset_offset] + file_start;
|
||||
String finalname = getNullDelimitedString(link, finalname_offset, encoding);
|
||||
if (IsLocal)
|
||||
{
|
||||
int basename_offset = link[file_start + basename_offset_offset] + file_start;
|
||||
String basename = getNullDelimitedString(link, basename_offset, encoding);
|
||||
ResolvedPath = basename + finalname;
|
||||
}
|
||||
else
|
||||
{
|
||||
int networkVolumeTable_offset = link[file_start + networkVolumeTable_offset_offset] + file_start;
|
||||
int shareName_offset_offset = 0x08;
|
||||
int shareName_offset = link[networkVolumeTable_offset + shareName_offset_offset]
|
||||
+ networkVolumeTable_offset;
|
||||
String shareName = getNullDelimitedString(link, shareName_offset, encoding);
|
||||
ResolvedPath = shareName + "\\" + finalname;
|
||||
}
|
||||
}
|
||||
|
||||
private static string getNullDelimitedString(byte[] bytes, int off, Encoding encoding)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
// count bytes until the null character (0)
|
||||
while (true)
|
||||
{
|
||||
if (bytes[off + len] == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
|
||||
return encoding.GetString(bytes, off, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* convert two bytes into a short note, this is little endian because it's
|
||||
* for an Intel only OS.
|
||||
*/
|
||||
private static int bytesToWord(byte[] bytes, int off)
|
||||
{
|
||||
return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff);
|
||||
}
|
||||
|
||||
private static int bytesToDword(byte[] bytes, int off)
|
||||
{
|
||||
return (bytesToWord(bytes, off + 2) << 16) | bytesToWord(bytes, off);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -81,5 +81,27 @@ namespace MediaBrowser.Common.IO
|
|||
/// <param name="file1">The file1.</param>
|
||||
/// <param name="file2">The file2.</param>
|
||||
void SwapFiles(string file1, string file2);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether [contains sub path] [the specified parent path].
|
||||
/// </summary>
|
||||
/// <param name="parentPath">The parent path.</param>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns><c>true</c> if [contains sub path] [the specified parent path]; otherwise, <c>false</c>.</returns>
|
||||
bool ContainsSubPath(string parentPath, string path);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether [is root path] [the specified path].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns><c>true</c> if [is root path] [the specified path]; otherwise, <c>false</c>.</returns>
|
||||
bool IsRootPath(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
string NormalizePath(string path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Net;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
@ -35,7 +36,7 @@ namespace MediaBrowser.Common.Net
|
|||
/// Gets available devices within the domain
|
||||
/// </summary>
|
||||
/// <returns>PC's in the Domain</returns>
|
||||
IEnumerable<string> GetNetworkDevices();
|
||||
IEnumerable<FileSystemEntryInfo> GetNetworkDevices();
|
||||
|
||||
/// <summary>
|
||||
/// Parses the specified endpointstring.
|
||||
|
|
|
@ -37,8 +37,10 @@ namespace MediaBrowser.Controller.Drawing
|
|||
|
||||
public bool AddPlayedIndicator { get; set; }
|
||||
|
||||
public int? PercentPlayed { get; set; }
|
||||
public int? UnplayedCount { get; set; }
|
||||
|
||||
public double? PercentPlayed { get; set; }
|
||||
|
||||
public string BackgroundColor { get; set; }
|
||||
|
||||
public bool HasDefaultOptions()
|
||||
|
@ -56,6 +58,7 @@ namespace MediaBrowser.Controller.Drawing
|
|||
IsOutputFormatDefault &&
|
||||
!AddPlayedIndicator &&
|
||||
!PercentPlayed.HasValue &&
|
||||
!UnplayedCount.HasValue &&
|
||||
string.IsNullOrEmpty(BackgroundColor);
|
||||
}
|
||||
|
||||
|
|
|
@ -156,7 +156,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
public DateTime DateModified { get; set; }
|
||||
|
||||
public DateTime DateLastSaved { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The logger
|
||||
/// </summary>
|
||||
|
@ -327,21 +327,18 @@ namespace MediaBrowser.Controller.Entities
|
|||
// When resolving the root, we need it's grandchildren (children of user views)
|
||||
var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
|
||||
|
||||
args.FileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, FileSystem, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
|
||||
var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, FileSystem, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
|
||||
|
||||
// Need to remove subpaths that may have been resolved from shortcuts
|
||||
// Example: if \\server\movies exists, then strip out \\server\movies\action
|
||||
if (isPhysicalRoot)
|
||||
{
|
||||
var paths = args.FileSystemDictionary.Keys.ToList();
|
||||
var paths = LibraryManager.NormalizeRootPathList(fileSystemDictionary.Keys);
|
||||
|
||||
foreach (var subPath in paths
|
||||
.Where(subPath => !subPath.EndsWith(":\\", StringComparison.OrdinalIgnoreCase) && paths.Any(i => subPath.StartsWith(i.TrimEnd(System.IO.Path.DirectorySeparatorChar) + System.IO.Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))))
|
||||
{
|
||||
Logger.Info("Ignoring duplicate path: {0}", subPath);
|
||||
args.FileSystemDictionary.Remove(subPath);
|
||||
}
|
||||
fileSystemDictionary = paths.Select(i => (FileSystemInfo)new DirectoryInfo(i)).ToDictionary(i => i.FullName);
|
||||
}
|
||||
|
||||
args.FileSystemDictionary = fileSystemDictionary;
|
||||
}
|
||||
|
||||
//update our dates
|
||||
|
@ -1016,14 +1013,18 @@ namespace MediaBrowser.Controller.Entities
|
|||
return lang;
|
||||
}
|
||||
|
||||
public virtual bool IsSaveLocalMetadataEnabled()
|
||||
{
|
||||
return ConfigurationManager.Configuration.SaveLocalMeta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a given user has access to this item
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="localizationManager">The localization manager.</param>
|
||||
/// <returns><c>true</c> if [is parental allowed] [the specified user]; otherwise, <c>false</c>.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">user</exception>
|
||||
public bool IsParentalAllowed(User user, ILocalizationManager localizationManager)
|
||||
public bool IsParentalAllowed(User user)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
|
@ -1049,7 +1050,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
return !GetBlockUnratedValue(user.Configuration);
|
||||
}
|
||||
|
||||
var value = localizationManager.GetRatingLevel(rating);
|
||||
var value = LocalizationManager.GetRatingLevel(rating);
|
||||
|
||||
// Could not determine the integer value
|
||||
if (!value.HasValue)
|
||||
|
@ -1084,7 +1085,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
throw new ArgumentNullException("user");
|
||||
}
|
||||
|
||||
return IsParentalAllowed(user, LocalizationManager);
|
||||
return IsParentalAllowed(user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -519,80 +519,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Tuple<BaseItem, bool> currentTuple = tuple;
|
||||
|
||||
tasks.Add(Task.Run(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var child = currentTuple.Item1;
|
||||
try
|
||||
{
|
||||
//refresh it
|
||||
await child.RefreshMetadata(cancellationToken, forceSave: currentTuple.Item2, forceRefresh: forceRefreshMetadata, resetResolveArgs: false).ConfigureAwait(false);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logger.ErrorException("Error refreshing {0}", ex, child.Path ?? child.Name);
|
||||
}
|
||||
|
||||
// Refresh children if a folder and the item changed or recursive is set to true
|
||||
var refreshChildren = child.IsFolder && (currentTuple.Item2 || (recursive.HasValue && recursive.Value));
|
||||
|
||||
if (refreshChildren)
|
||||
{
|
||||
// Don't refresh children if explicitly set to false
|
||||
if (recursive.HasValue && recursive.Value == false)
|
||||
{
|
||||
refreshChildren = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (refreshChildren)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var innerProgress = new ActionableProgress<double>();
|
||||
|
||||
innerProgress.RegisterAction(p =>
|
||||
{
|
||||
lock (percentages)
|
||||
{
|
||||
percentages[child.Id] = p / 100;
|
||||
|
||||
var percent = percentages.Values.Sum();
|
||||
percent /= list.Count;
|
||||
|
||||
progress.Report((90 * percent) + 10);
|
||||
}
|
||||
});
|
||||
|
||||
await ((Folder)child).ValidateChildren(innerProgress, cancellationToken, recursive, forceRefreshMetadata).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
// Some folder providers are unable to refresh until children have been refreshed.
|
||||
await child.RefreshMetadata(cancellationToken, resetResolveArgs: false).ConfigureAwait(false);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logger.ErrorException("Error refreshing {0}", ex, child.Path ?? child.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (percentages)
|
||||
{
|
||||
percentages[child.Id] = 1;
|
||||
|
||||
var percent = percentages.Values.Sum();
|
||||
percent /= list.Count;
|
||||
|
||||
progress.Report((90 * percent) + 10);
|
||||
}
|
||||
}
|
||||
|
||||
}, cancellationToken));
|
||||
tasks.Add(RefreshChild(tuple, progress, percentages, list.Count, cancellationToken, recursive, forceRefreshMetadata));
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
@ -600,6 +527,78 @@ namespace MediaBrowser.Controller.Entities
|
|||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task RefreshChild(Tuple<BaseItem, bool> currentTuple, IProgress<double> progress, Dictionary<Guid, double> percentages, int childCount, CancellationToken cancellationToken, bool? recursive, bool forceRefreshMetadata = false)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var child = currentTuple.Item1;
|
||||
try
|
||||
{
|
||||
//refresh it
|
||||
await child.RefreshMetadata(cancellationToken, forceSave: currentTuple.Item2, forceRefresh: forceRefreshMetadata, resetResolveArgs: false).ConfigureAwait(false);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logger.ErrorException("Error refreshing {0}", ex, child.Path ?? child.Name);
|
||||
}
|
||||
|
||||
// Refresh children if a folder and the item changed or recursive is set to true
|
||||
var refreshChildren = child.IsFolder && (currentTuple.Item2 || (recursive.HasValue && recursive.Value));
|
||||
|
||||
if (refreshChildren)
|
||||
{
|
||||
// Don't refresh children if explicitly set to false
|
||||
if (recursive.HasValue && recursive.Value == false)
|
||||
{
|
||||
refreshChildren = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (refreshChildren)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var innerProgress = new ActionableProgress<double>();
|
||||
|
||||
innerProgress.RegisterAction(p =>
|
||||
{
|
||||
lock (percentages)
|
||||
{
|
||||
percentages[child.Id] = p / 100;
|
||||
|
||||
var percent = percentages.Values.Sum();
|
||||
percent /= childCount;
|
||||
|
||||
progress.Report((90 * percent) + 10);
|
||||
}
|
||||
});
|
||||
|
||||
await ((Folder)child).ValidateChildren(innerProgress, cancellationToken, recursive, forceRefreshMetadata).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
// Some folder providers are unable to refresh until children have been refreshed.
|
||||
await child.RefreshMetadata(cancellationToken, resetResolveArgs: false).ConfigureAwait(false);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logger.ErrorException("Error refreshing {0}", ex, child.Path ?? child.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (percentages)
|
||||
{
|
||||
percentages[child.Id] = 1;
|
||||
|
||||
var percent = percentages.Values.Sum();
|
||||
percent /= childCount;
|
||||
|
||||
progress.Report((90 * percent) + 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified path is offline.
|
||||
/// </summary>
|
||||
|
@ -646,7 +645,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
private bool ContainsPath(string parent, string path)
|
||||
{
|
||||
return string.Equals(parent, path, StringComparison.OrdinalIgnoreCase) || path.IndexOf(parent.TrimEnd(System.IO.Path.DirectorySeparatorChar) + System.IO.Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) != -1;
|
||||
return string.Equals(parent, path, StringComparison.OrdinalIgnoreCase) || FileSystem.ContainsSubPath(parent, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -16,8 +16,8 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// Gets the path.
|
||||
/// </summary>
|
||||
/// <value>The path.</value>
|
||||
string Path { get; }
|
||||
|
||||
string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the identifier.
|
||||
/// </summary>
|
||||
|
@ -100,7 +100,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
return item.HasImage(imageType, 0);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the image path.
|
||||
/// </summary>
|
||||
|
|
|
@ -320,5 +320,12 @@ namespace MediaBrowser.Controller.Library
|
|||
/// <param name="items">The items.</param>
|
||||
/// <returns>IEnumerable{System.String}.</returns>
|
||||
IEnumerable<string> GetAllArtists(IEnumerable<BaseItem> items);
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the root path list.
|
||||
/// </summary>
|
||||
/// <param name="paths">The paths.</param>
|
||||
/// <returns>IEnumerable{System.String}.</returns>
|
||||
IEnumerable<string> NormalizeRootPathList(IEnumerable<string> paths);
|
||||
}
|
||||
}
|
|
@ -153,7 +153,7 @@ namespace MediaBrowser.Controller.LiveTv
|
|||
/// <param name="id">The identifier.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>LiveTvRecording.</returns>
|
||||
Task<LiveTvRecording> GetInternalRecording(string id, CancellationToken cancellationToken);
|
||||
Task<ILiveTvRecording> GetInternalRecording(string id, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the recording stream.
|
||||
|
|
26
MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
Normal file
26
MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Controller.LiveTv
|
||||
{
|
||||
public interface ILiveTvRecording : IHasImages, IHasMediaStreams
|
||||
{
|
||||
string ServiceName { get; set; }
|
||||
|
||||
string MediaType { get; }
|
||||
|
||||
LocationType LocationType { get; }
|
||||
|
||||
RecordingInfo RecordingInfo { get; set; }
|
||||
|
||||
string GetClientTypeName();
|
||||
|
||||
string GetUserDataKey();
|
||||
|
||||
bool IsParentalAllowed(User user);
|
||||
|
||||
Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true);
|
||||
}
|
||||
}
|
52
MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
Normal file
52
MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
Normal file
|
@ -0,0 +1,52 @@
|
|||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
||||
namespace MediaBrowser.Controller.LiveTv
|
||||
{
|
||||
public class LiveTvAudioRecording : Audio, ILiveTvRecording
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the user data key.
|
||||
/// </summary>
|
||||
/// <returns>System.String.</returns>
|
||||
public override string GetUserDataKey()
|
||||
{
|
||||
return GetClientTypeName() + "-" + Name;
|
||||
}
|
||||
|
||||
public RecordingInfo RecordingInfo { get; set; }
|
||||
|
||||
public string ServiceName { get; set; }
|
||||
|
||||
public override string MediaType
|
||||
{
|
||||
get
|
||||
{
|
||||
return Model.Entities.MediaType.Audio;
|
||||
}
|
||||
}
|
||||
|
||||
public override LocationType LocationType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Path))
|
||||
{
|
||||
return base.LocationType;
|
||||
}
|
||||
|
||||
return LocationType.Remote;
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetClientTypeName()
|
||||
{
|
||||
return "Recording";
|
||||
}
|
||||
|
||||
public override bool IsSaveLocalMetadataEnabled()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
|
||||
namespace MediaBrowser.Controller.LiveTv
|
||||
{
|
||||
public class LiveTvRecording : BaseItem
|
||||
public class LiveTvVideoRecording : Video, ILiveTvRecording
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the user data key.
|
||||
|
@ -23,7 +22,7 @@ namespace MediaBrowser.Controller.LiveTv
|
|||
{
|
||||
get
|
||||
{
|
||||
return RecordingInfo.ChannelType == ChannelType.Radio ? Model.Entities.MediaType.Audio : Model.Entities.MediaType.Video;
|
||||
return Model.Entities.MediaType.Video;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,6 +30,11 @@ namespace MediaBrowser.Controller.LiveTv
|
|||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Path))
|
||||
{
|
||||
return base.LocationType;
|
||||
}
|
||||
|
||||
return LocationType.Remote;
|
||||
}
|
||||
}
|
||||
|
@ -39,5 +43,10 @@ namespace MediaBrowser.Controller.LiveTv
|
|||
{
|
||||
return "Recording";
|
||||
}
|
||||
|
||||
public override bool IsSaveLocalMetadataEnabled()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -108,6 +108,8 @@
|
|||
<Compile Include="Library\ItemUpdateType.cs" />
|
||||
<Compile Include="Library\IUserDataManager.cs" />
|
||||
<Compile Include="Library\UserDataSaveEventArgs.cs" />
|
||||
<Compile Include="LiveTv\ILiveTvRecording.cs" />
|
||||
<Compile Include="LiveTv\LiveTvAudioRecording.cs" />
|
||||
<Compile Include="LiveTv\LiveTvChannel.cs" />
|
||||
<Compile Include="LiveTv\ChannelInfo.cs" />
|
||||
<Compile Include="LiveTv\ILiveTvManager.cs" />
|
||||
|
@ -115,7 +117,7 @@
|
|||
<Compile Include="LiveTv\LiveTvException.cs" />
|
||||
<Compile Include="LiveTv\StreamResponseInfo.cs" />
|
||||
<Compile Include="LiveTv\LiveTvProgram.cs" />
|
||||
<Compile Include="LiveTv\LiveTvRecording.cs" />
|
||||
<Compile Include="LiveTv\LiveTvVideoRecording.cs" />
|
||||
<Compile Include="LiveTv\ProgramInfo.cs" />
|
||||
<Compile Include="LiveTv\RecordingInfo.cs" />
|
||||
<Compile Include="LiveTv\SeriesTimerInfo.cs" />
|
||||
|
|
|
@ -51,6 +51,18 @@ namespace MediaBrowser.Model.LiveTv
|
|||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the location.
|
||||
/// </summary>
|
||||
/// <value>The type of the location.</value>
|
||||
public LocationType LocationType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the media streams.
|
||||
/// </summary>
|
||||
/// <value>The media streams.</value>
|
||||
public List<MediaStream> MediaStreams { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path.
|
||||
/// </summary>
|
||||
|
|
|
@ -40,6 +40,12 @@
|
|||
/// </summary>
|
||||
/// <value>The limit.</value>
|
||||
public int? Limit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is recording.
|
||||
/// </summary>
|
||||
/// <value><c>null</c> if [is recording] contains no value, <c>true</c> if [is recording]; otherwise, <c>false</c>.</value>
|
||||
public bool? IsRecording { get; set; }
|
||||
}
|
||||
|
||||
public class RecordingGroupQuery
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<Properties>
|
||||
<MonoDevelop.Ide.Workspace ActiveConfiguration="Release Mono" />
|
||||
<MonoDevelop.Ide.Workbench ActiveDocument="MediaBrowser.Server.Mono\app.config">
|
||||
<MonoDevelop.Ide.Workbench ActiveDocument="MediaBrowser.Server.Mono\Networking\NetworkManager.cs">
|
||||
<Files>
|
||||
<File FileName="MediaBrowser.Server.Mono\app.config" Line="5" Column="20" />
|
||||
<File FileName="MediaBrowser.Server.Mono\app.config" Line="1" Column="1" />
|
||||
<File FileName="MediaBrowser.Server.Mono\Networking\NetworkManager.cs" Line="6" Column="34" />
|
||||
</Files>
|
||||
</MonoDevelop.Ide.Workbench>
|
||||
<MonoDevelop.Ide.DebuggingService.Breakpoints>
|
||||
|
|
|
@ -191,7 +191,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||
|
||||
var filename = item.Album ?? string.Empty;
|
||||
filename += item.Artists.FirstOrDefault() ?? string.Empty;
|
||||
filename += album == null ? item.Id.ToString("N") + item.DateModified.Ticks : album.Id.ToString("N") + album.DateModified.Ticks + "_primary";
|
||||
filename += album == null ? item.Id.ToString("N") + "_primary" + item.DateModified.Ticks : album.Id.ToString("N") + album.DateModified.Ticks + "_primary";
|
||||
|
||||
filename = filename.GetMD5() + ".jpg";
|
||||
|
||||
|
|
|
@ -85,15 +85,6 @@ namespace MediaBrowser.Providers.Music
|
|||
return item is MusicArtist;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether [save local meta].
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if [save local meta]; otherwise, <c>false</c>.</value>
|
||||
protected virtual bool SaveLocalMeta
|
||||
{
|
||||
get { return ConfigurationManager.Configuration.SaveLocalMeta; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether [refresh on version change].
|
||||
/// </summary>
|
||||
|
|
|
@ -68,14 +68,6 @@ namespace MediaBrowser.Providers.Music
|
|||
/// <value>The HTTP client.</value>
|
||||
protected IHttpClient HttpClient { get; private set; }
|
||||
|
||||
protected virtual bool SaveLocalMeta
|
||||
{
|
||||
get
|
||||
{
|
||||
return ConfigurationManager.Configuration.SaveLocalMeta;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether [requires internet].
|
||||
/// </summary>
|
||||
|
|
|
@ -31,11 +31,6 @@ namespace MediaBrowser.Providers.Music
|
|||
|
||||
artist.ProductionYear = yearFormed;
|
||||
}
|
||||
|
||||
if (data.tags != null && !artist.LockedFields.Contains(MetadataFields.Tags))
|
||||
{
|
||||
AddTags(artist, data.tags);
|
||||
}
|
||||
|
||||
string imageSize;
|
||||
artist.LastFmImageUrl = GetImageUrl(data, out imageSize);
|
||||
|
@ -100,11 +95,6 @@ namespace MediaBrowser.Providers.Music
|
|||
}
|
||||
}
|
||||
|
||||
if (data.toptags != null && !item.LockedFields.Contains(MetadataFields.Tags))
|
||||
{
|
||||
AddTags(item, data.toptags);
|
||||
}
|
||||
|
||||
var album = (MusicAlbum)item;
|
||||
|
||||
string imageSize;
|
||||
|
@ -112,16 +102,5 @@ namespace MediaBrowser.Providers.Music
|
|||
album.LastFmImageUrl = GetImageUrl(data, out imageSize);
|
||||
album.LastFmImageSize = imageSize;
|
||||
}
|
||||
|
||||
private static void AddTags(BaseItem item, LastfmTags tags)
|
||||
{
|
||||
var itemTags = (from tag in tags.tag where !string.IsNullOrEmpty(tag.name) select tag.name).ToList();
|
||||
|
||||
var hasTags = item as IHasTags;
|
||||
if (hasTags != null)
|
||||
{
|
||||
hasTags.Tags = itemTags;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace MediaBrowser.Providers.Savers
|
|||
var wasMetadataDownloaded = (updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload;
|
||||
|
||||
// If new metadata has been downloaded and save local is on
|
||||
if (_config.Configuration.SaveLocalMeta && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
if (item.IsSaveLocalMetadataEnabled() && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
{
|
||||
return item is MusicAlbum;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace MediaBrowser.Providers.Savers
|
|||
var wasMetadataDownloaded = (updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload;
|
||||
|
||||
// If new metadata has been downloaded and save local is on
|
||||
if (_config.Configuration.SaveLocalMeta && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
if (item.IsSaveLocalMetadataEnabled() && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
{
|
||||
if (item is MusicArtist)
|
||||
{
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace MediaBrowser.Providers.Savers
|
|||
var wasMetadataDownloaded = (updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload;
|
||||
|
||||
// If new metadata has been downloaded and save local is on
|
||||
if (_config.Configuration.SaveLocalMeta && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
if (item.IsSaveLocalMetadataEnabled() && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
{
|
||||
return item is BoxSet;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace MediaBrowser.Providers.Savers
|
|||
var wasMetadataDownloaded = (updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload;
|
||||
|
||||
// If new metadata has been downloaded and save local is on
|
||||
if (_config.Configuration.SaveLocalMeta && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
if (item.IsSaveLocalMetadataEnabled() && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
{
|
||||
return item is Episode;
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace MediaBrowser.Providers.Savers
|
|||
var wasMetadataDownloaded = (updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload;
|
||||
|
||||
// If new metadata has been downloaded and save local is on
|
||||
if (_config.Configuration.SaveLocalMeta && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
if (item.IsSaveLocalMetadataEnabled() && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
{
|
||||
if (!(item is Series) && !(item is BoxSet) && !(item is MusicArtist) && !(item is MusicAlbum) &&
|
||||
!(item is Season))
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace MediaBrowser.Providers.Savers
|
|||
var wasMetadataDownloaded = (updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload;
|
||||
|
||||
// If new metadata has been downloaded and save local is on
|
||||
if (_config.Configuration.SaveLocalMeta && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
if (item.IsSaveLocalMetadataEnabled() && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
{
|
||||
return item is GameSystem;
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace MediaBrowser.Providers.Savers
|
|||
var wasMetadataDownloaded = (updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload;
|
||||
|
||||
// If new metadata has been downloaded and save local is on
|
||||
if (_config.Configuration.SaveLocalMeta && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
if (item.IsSaveLocalMetadataEnabled() && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
{
|
||||
return item is Game;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace MediaBrowser.Providers.Savers
|
|||
var wasMetadataDownloaded = (updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload;
|
||||
|
||||
// If new metadata has been downloaded and save local is on
|
||||
if (_config.Configuration.SaveLocalMeta && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
if (item.IsSaveLocalMetadataEnabled() && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
{
|
||||
var trailer = item as Trailer;
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace MediaBrowser.Providers.Savers
|
|||
var wasMetadataDownloaded = (updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload;
|
||||
|
||||
// If new metadata has been downloaded and save local is on
|
||||
if (_config.Configuration.SaveLocalMeta && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
if (item.IsSaveLocalMetadataEnabled() && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
{
|
||||
return item is Season;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace MediaBrowser.Providers.Savers
|
|||
var wasMetadataDownloaded = (updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload;
|
||||
|
||||
// If new metadata has been downloaded and save local is on
|
||||
if (_config.Configuration.SaveLocalMeta && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
if (item.IsSaveLocalMetadataEnabled() && (wasMetadataEdited || wasMetadataDownloaded))
|
||||
{
|
||||
return item is Series;
|
||||
}
|
||||
|
|
|
@ -172,7 +172,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
|
|||
|
||||
var quality = options.Quality ?? 90;
|
||||
|
||||
var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, options.OutputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.BackgroundColor);
|
||||
var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, options.OutputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.BackgroundColor);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -241,7 +241,9 @@ namespace MediaBrowser.Server.Implementations.Drawing
|
|||
thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality;
|
||||
thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
thumbnailGraph.CompositingMode = string.IsNullOrEmpty(options.BackgroundColor) && !options.PercentPlayed.HasValue && !options.AddPlayedIndicator ? CompositingMode.SourceCopy : CompositingMode.SourceOver;
|
||||
thumbnailGraph.CompositingMode = string.IsNullOrEmpty(options.BackgroundColor) && !options.UnplayedCount.HasValue && !options.AddPlayedIndicator && !options.PercentPlayed.HasValue ?
|
||||
CompositingMode.SourceCopy :
|
||||
CompositingMode.SourceOver;
|
||||
|
||||
SetBackgroundColor(thumbnailGraph, options);
|
||||
|
||||
|
@ -347,28 +349,31 @@ namespace MediaBrowser.Server.Implementations.Drawing
|
|||
/// <param name="options">The options.</param>
|
||||
private void DrawIndicator(Graphics graphics, int imageWidth, int imageHeight, ImageProcessingOptions options)
|
||||
{
|
||||
if (!options.AddPlayedIndicator && !options.PercentPlayed.HasValue)
|
||||
if (!options.AddPlayedIndicator && !options.UnplayedCount.HasValue && !options.PercentPlayed.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var percentOffset = 0;
|
||||
|
||||
if (options.AddPlayedIndicator)
|
||||
{
|
||||
var currentImageSize = new Size(imageWidth, imageHeight);
|
||||
|
||||
new WatchedIndicatorDrawer().Process(graphics, currentImageSize);
|
||||
|
||||
percentOffset = 0 - WatchedIndicatorDrawer.IndicatorWidth;
|
||||
new PlayedIndicatorDrawer().DrawPlayedIndicator(graphics, currentImageSize);
|
||||
}
|
||||
else if (options.UnplayedCount.HasValue)
|
||||
{
|
||||
var currentImageSize = new Size(imageWidth, imageHeight);
|
||||
|
||||
new UnplayedCountIndicator().DrawUnplayedCountIndicator(graphics, currentImageSize, options.UnplayedCount.Value);
|
||||
}
|
||||
|
||||
if (options.PercentPlayed.HasValue)
|
||||
{
|
||||
var currentImageSize = new Size(imageWidth, imageHeight);
|
||||
|
||||
new PercentPlayedDrawer().Process(graphics, currentImageSize, options.PercentPlayed.Value, percentOffset);
|
||||
new PercentPlayedDrawer().Process(graphics, currentImageSize, options.PercentPlayed.Value);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -465,10 +470,15 @@ namespace MediaBrowser.Server.Implementations.Drawing
|
|||
return new Tuple<string, DateTime>(croppedImagePath, _fileSystem.GetLastWriteTimeUtc(croppedImagePath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increment this when indicator drawings change
|
||||
/// </summary>
|
||||
private const string IndicatorVersion = "1";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cache file path based on a set of parameters
|
||||
/// </summary>
|
||||
private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageOutputFormat format, bool addPlayedIndicator, int? percentPlayed, string backgroundColor)
|
||||
private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageOutputFormat format, bool addPlayedIndicator, double? percentPlayed, int? unwatchedCount, string backgroundColor)
|
||||
{
|
||||
var filename = originalPath;
|
||||
|
||||
|
@ -485,16 +495,31 @@ namespace MediaBrowser.Server.Implementations.Drawing
|
|||
filename += "f=" + format;
|
||||
}
|
||||
|
||||
var hasIndicator = false;
|
||||
|
||||
if (addPlayedIndicator)
|
||||
{
|
||||
filename += "pl=true";
|
||||
hasIndicator = true;
|
||||
}
|
||||
|
||||
if (percentPlayed.HasValue)
|
||||
{
|
||||
filename += "p=" + percentPlayed.Value;
|
||||
hasIndicator = true;
|
||||
}
|
||||
|
||||
if (unwatchedCount.HasValue)
|
||||
{
|
||||
filename += "p=" + unwatchedCount.Value;
|
||||
hasIndicator = true;
|
||||
}
|
||||
|
||||
if (hasIndicator)
|
||||
{
|
||||
filename += "iv=" + IndicatorVersion;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(backgroundColor))
|
||||
{
|
||||
filename += "b=" + backgroundColor;
|
||||
|
|
|
@ -1,36 +1,34 @@
|
|||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.Drawing
|
||||
{
|
||||
public class PercentPlayedDrawer
|
||||
{
|
||||
private const int IndicatorWidth = 80;
|
||||
private const int IndicatorHeight = 50;
|
||||
private const int FontSize = 30;
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
private const int IndicatorHeight = 10;
|
||||
|
||||
public void Process(Graphics graphics, Size imageSize, int percent, int rightOffset)
|
||||
public void Process(Graphics graphics, Size imageSize, double percent)
|
||||
{
|
||||
var x = imageSize.Width - IndicatorWidth + rightOffset;
|
||||
var y = imageSize.Height - IndicatorHeight;
|
||||
|
||||
using (var backdroundBrush = new SolidBrush(Color.FromArgb(225, 102, 192, 16)))
|
||||
using (var backdroundBrush = new SolidBrush(Color.FromArgb(225, 0, 0, 0)))
|
||||
{
|
||||
graphics.FillRectangle(backdroundBrush, x, 0, IndicatorWidth, IndicatorHeight);
|
||||
const int innerX = 0;
|
||||
var innerY = y;
|
||||
var innerWidth = imageSize.Width;
|
||||
var innerHeight = imageSize.Height;
|
||||
|
||||
var text = string.Format("{0}%", percent.ToString(_usCulture));
|
||||
graphics.FillRectangle(backdroundBrush, innerX, innerY, innerWidth, innerHeight);
|
||||
|
||||
x = imageSize.Width - (percent < 10 ? 66 : 75) + rightOffset;
|
||||
|
||||
using (var font = new Font(FontFamily.GenericSansSerif, FontSize, FontStyle.Regular, GraphicsUnit.Pixel))
|
||||
using (var foregroundBrush = new SolidBrush(Color.FromArgb(82, 181, 75)))
|
||||
{
|
||||
using (var fontBrush = new SolidBrush(Color.White))
|
||||
{
|
||||
graphics.DrawString(text, font, fontBrush, x, 6);
|
||||
}
|
||||
double foregroundWidth = innerWidth;
|
||||
foregroundWidth *= percent;
|
||||
foregroundWidth /= 100;
|
||||
|
||||
graphics.FillRectangle(foregroundBrush, innerX, innerY, Convert.ToInt32(Math.Round(foregroundWidth)), innerHeight);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,33 +2,31 @@
|
|||
|
||||
namespace MediaBrowser.Server.Implementations.Drawing
|
||||
{
|
||||
public class WatchedIndicatorDrawer
|
||||
public class PlayedIndicatorDrawer
|
||||
{
|
||||
private const int IndicatorHeight = 50;
|
||||
public const int IndicatorWidth = 50;
|
||||
private const int FontSize = 50;
|
||||
private const int OffsetFromTopRightCorner = 10;
|
||||
|
||||
public void Process(Graphics graphics, Size imageSize)
|
||||
public void DrawPlayedIndicator(Graphics graphics, Size imageSize)
|
||||
{
|
||||
var x = imageSize.Width - IndicatorWidth;
|
||||
var x = imageSize.Width - IndicatorWidth - OffsetFromTopRightCorner;
|
||||
|
||||
using (var backdroundBrush = new SolidBrush(Color.FromArgb(225, 204, 51, 51)))
|
||||
using (var backdroundBrush = new SolidBrush(Color.FromArgb(225, 82, 181, 75)))
|
||||
{
|
||||
graphics.FillRectangle(backdroundBrush, x, 0, IndicatorWidth, IndicatorHeight);
|
||||
graphics.FillEllipse(backdroundBrush, x, OffsetFromTopRightCorner, IndicatorWidth, IndicatorHeight);
|
||||
|
||||
const string text = "a";
|
||||
|
||||
x = imageSize.Width - 55;
|
||||
x = imageSize.Width - 55 - OffsetFromTopRightCorner;
|
||||
|
||||
using (var font = new Font("Webdings", FontSize, FontStyle.Regular, GraphicsUnit.Pixel))
|
||||
{
|
||||
using (var fontBrush = new SolidBrush(Color.White))
|
||||
{
|
||||
graphics.DrawString(text, font, fontBrush, x, -2);
|
||||
graphics.DrawString("a", font, fontBrush, x, OffsetFromTopRightCorner - 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.Drawing
|
||||
{
|
||||
public class UnplayedCountIndicator
|
||||
{
|
||||
private const int IndicatorHeight = 50;
|
||||
public const int IndicatorWidth = 50;
|
||||
private const int OffsetFromTopRightCorner = 10;
|
||||
|
||||
public void DrawUnplayedCountIndicator(Graphics graphics, Size imageSize, int count)
|
||||
{
|
||||
var x = imageSize.Width - IndicatorWidth - OffsetFromTopRightCorner;
|
||||
|
||||
using (var backdroundBrush = new SolidBrush(Color.FromArgb(225, 82, 181, 75)))
|
||||
{
|
||||
graphics.FillEllipse(backdroundBrush, x, OffsetFromTopRightCorner, IndicatorWidth, IndicatorHeight);
|
||||
|
||||
var text = count.ToString();
|
||||
|
||||
x = imageSize.Width - 50 - OffsetFromTopRightCorner;
|
||||
var y = OffsetFromTopRightCorner + 7;
|
||||
var fontSize = 30;
|
||||
|
||||
if (text.Length == 1)
|
||||
{
|
||||
x += 11;
|
||||
}
|
||||
else if (text.Length == 2)
|
||||
{
|
||||
x += 3;
|
||||
}
|
||||
else if (text.Length == 3)
|
||||
{
|
||||
//x += 1;
|
||||
y += 3;
|
||||
fontSize = 24;
|
||||
}
|
||||
|
||||
using (var font = new Font("Sans-Serif", fontSize, FontStyle.Regular, GraphicsUnit.Pixel))
|
||||
{
|
||||
using (var fontBrush = new SolidBrush(Color.White))
|
||||
{
|
||||
graphics.DrawString(text, font, fontBrush, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Resolvers;
|
||||
using System;
|
||||
|
@ -30,6 +31,13 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
|
||||
}.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public CoreResolutionIgnoreRule(IFileSystem fileSystem)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shoulds the ignore.
|
||||
/// </summary>
|
||||
|
@ -60,23 +68,12 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
return false;
|
||||
}
|
||||
|
||||
// Drives will sometimes be hidden
|
||||
if (args.Path.EndsWith(Path.VolumeSeparatorChar + "\\", StringComparison.OrdinalIgnoreCase))
|
||||
// Sometimes these are marked hidden
|
||||
if (_fileSystem.IsRootPath(args.Path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Shares will sometimes be hidden
|
||||
if (args.Path.StartsWith("\\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Look for a share, e.g. \\server\movies
|
||||
// Is there a better way to detect if a path is a share without using native code?
|
||||
if (args.Path.Substring(2).Split(Path.DirectorySeparatorChar).Length == 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -499,21 +499,18 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
// When resolving the root, we need it's grandchildren (children of user views)
|
||||
var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
|
||||
|
||||
args.FileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, _fileSystem, _logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
|
||||
var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, _fileSystem, _logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
|
||||
|
||||
// Need to remove subpaths that may have been resolved from shortcuts
|
||||
// Example: if \\server\movies exists, then strip out \\server\movies\action
|
||||
if (isPhysicalRoot)
|
||||
{
|
||||
var paths = args.FileSystemDictionary.Keys.ToList();
|
||||
var paths = NormalizeRootPathList(fileSystemDictionary.Keys);
|
||||
|
||||
foreach (var subPath in paths
|
||||
.Where(subPath => !subPath.EndsWith(":\\", StringComparison.OrdinalIgnoreCase) && paths.Any(i => subPath.StartsWith(i.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))))
|
||||
{
|
||||
_logger.Info("Ignoring duplicate path: {0}", subPath);
|
||||
args.FileSystemDictionary.Remove(subPath);
|
||||
}
|
||||
fileSystemDictionary = paths.Select(i => (FileSystemInfo)new DirectoryInfo(i)).ToDictionary(i => i.FullName);
|
||||
}
|
||||
|
||||
args.FileSystemDictionary = fileSystemDictionary;
|
||||
}
|
||||
|
||||
// Check to see if we should resolve based on our contents
|
||||
|
@ -525,6 +522,23 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
return ResolveItem(args);
|
||||
}
|
||||
|
||||
public IEnumerable<string> NormalizeRootPathList(IEnumerable<string> paths)
|
||||
{
|
||||
var list = paths.Select(_fileSystem.NormalizePath)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
var dupes = list.Where(subPath => !subPath.EndsWith(":\\", StringComparison.OrdinalIgnoreCase) && list.Any(i => _fileSystem.ContainsSubPath(i, subPath)))
|
||||
.ToList();
|
||||
|
||||
foreach (var dupe in dupes)
|
||||
{
|
||||
_logger.Info("Found duplicate path: {0}", dupe);
|
||||
}
|
||||
|
||||
return list.Except(dupes, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a path should be ignored based on its contents - called after the contents have been read
|
||||
/// </summary>
|
||||
|
|
|
@ -4,11 +4,13 @@ using MediaBrowser.Controller.Dto;
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -21,13 +23,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
|
||||
private readonly IUserDataManager _userDataManager;
|
||||
private readonly IDtoService _dtoService;
|
||||
private readonly IItemRepository _itemRepo;
|
||||
|
||||
public LiveTvDtoService(IDtoService dtoService, IUserDataManager userDataManager, IImageProcessor imageProcessor, ILogger logger)
|
||||
public LiveTvDtoService(IDtoService dtoService, IUserDataManager userDataManager, IImageProcessor imageProcessor, ILogger logger, IItemRepository itemRepo)
|
||||
{
|
||||
_dtoService = dtoService;
|
||||
_userDataManager = userDataManager;
|
||||
_imageProcessor = imageProcessor;
|
||||
_logger = logger;
|
||||
_itemRepo = itemRepo;
|
||||
}
|
||||
|
||||
public TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service, LiveTvProgram program, LiveTvChannel channel)
|
||||
|
@ -180,7 +184,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
return status.ToString();
|
||||
}
|
||||
|
||||
public RecordingInfoDto GetRecordingInfoDto(LiveTvRecording recording, LiveTvChannel channel, ILiveTvService service, User user = null)
|
||||
public RecordingInfoDto GetRecordingInfoDto(ILiveTvRecording recording, LiveTvChannel channel, ILiveTvService service, User user = null)
|
||||
{
|
||||
var info = recording.RecordingInfo;
|
||||
|
||||
|
@ -216,7 +220,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
IsNews = info.IsNews,
|
||||
IsKids = info.IsKids,
|
||||
IsPremiere = info.IsPremiere,
|
||||
RunTimeTicks = (info.EndDate - info.StartDate).Ticks
|
||||
RunTimeTicks = (info.EndDate - info.StartDate).Ticks,
|
||||
LocationType = recording.LocationType,
|
||||
|
||||
MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery
|
||||
{
|
||||
ItemId = recording.Id
|
||||
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
var imageTag = GetImageTag(recording);
|
||||
|
@ -330,7 +341,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
return dto;
|
||||
}
|
||||
|
||||
private Guid? GetImageTag(BaseItem info)
|
||||
private Guid? GetImageTag(IHasImages info)
|
||||
{
|
||||
var path = info.PrimaryImagePath;
|
||||
|
||||
|
@ -351,39 +362,41 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
return null;
|
||||
}
|
||||
|
||||
private const string InternalVersionNumber = "2";
|
||||
|
||||
public Guid GetInternalChannelId(string serviceName, string externalId)
|
||||
{
|
||||
var name = serviceName + externalId;
|
||||
var name = serviceName + externalId + InternalVersionNumber;
|
||||
|
||||
return name.ToLower().GetMBId(typeof(LiveTvChannel));
|
||||
}
|
||||
|
||||
public Guid GetInternalTimerId(string serviceName, string externalId)
|
||||
{
|
||||
var name = serviceName + externalId;
|
||||
var name = serviceName + externalId + InternalVersionNumber;
|
||||
|
||||
return name.ToLower().GetMD5();
|
||||
}
|
||||
|
||||
public Guid GetInternalSeriesTimerId(string serviceName, string externalId)
|
||||
{
|
||||
var name = serviceName + externalId;
|
||||
var name = serviceName + externalId + InternalVersionNumber;
|
||||
|
||||
return name.ToLower().GetMD5();
|
||||
}
|
||||
|
||||
public Guid GetInternalProgramId(string serviceName, string externalId)
|
||||
{
|
||||
var name = serviceName + externalId;
|
||||
var name = serviceName + externalId + InternalVersionNumber;
|
||||
|
||||
return name.ToLower().GetMD5();
|
||||
return name.ToLower().GetMBId(typeof(LiveTvProgram));
|
||||
}
|
||||
|
||||
public Guid GetInternalRecordingId(string serviceName, string externalId)
|
||||
{
|
||||
var name = serviceName + externalId;
|
||||
var name = serviceName + externalId + InternalVersionNumber;
|
||||
|
||||
return name.ToLower().GetMD5();
|
||||
return name.ToLower().GetMBId(typeof(ILiveTvRecording));
|
||||
}
|
||||
|
||||
public async Task<TimerInfo> GetTimerInfo(TimerInfoDto dto, bool isNew, ILiveTvManager liveTv, CancellationToken cancellationToken)
|
||||
|
|
|
@ -6,8 +6,8 @@ using MediaBrowser.Controller.Dto;
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Controller.Localization;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Querying;
|
||||
|
@ -31,7 +31,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
private readonly IItemRepository _itemRepo;
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
private readonly ILocalizationManager _localization;
|
||||
private readonly LiveTvDtoService _tvDtoService;
|
||||
|
||||
private readonly List<ILiveTvService> _services = new List<ILiveTvService>();
|
||||
|
@ -39,16 +38,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
private Dictionary<Guid, LiveTvChannel> _channels = new Dictionary<Guid, LiveTvChannel>();
|
||||
private Dictionary<Guid, LiveTvProgram> _programs = new Dictionary<Guid, LiveTvProgram>();
|
||||
|
||||
public LiveTvManager(IServerApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, ILocalizationManager localization, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager)
|
||||
public LiveTvManager(IServerApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager)
|
||||
{
|
||||
_appPaths = appPaths;
|
||||
_fileSystem = fileSystem;
|
||||
_logger = logger;
|
||||
_itemRepo = itemRepo;
|
||||
_localization = localization;
|
||||
_userManager = userManager;
|
||||
|
||||
_tvDtoService = new LiveTvDtoService(dtoService, userDataManager, imageProcessor, logger);
|
||||
_tvDtoService = new LiveTvDtoService(dtoService, userDataManager, imageProcessor, logger, _itemRepo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -82,7 +80,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
if (user != null)
|
||||
{
|
||||
channels = channels
|
||||
.Where(i => i.IsParentalAllowed(user, _localization))
|
||||
.Where(i => i.IsParentalAllowed(user))
|
||||
.OrderBy(i =>
|
||||
{
|
||||
double number = 0;
|
||||
|
@ -144,7 +142,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
return obj;
|
||||
}
|
||||
|
||||
public async Task<LiveTvRecording> GetInternalRecording(string id, CancellationToken cancellationToken)
|
||||
public async Task<ILiveTvRecording> GetInternalRecording(string id, CancellationToken cancellationToken)
|
||||
{
|
||||
var service = ActiveService;
|
||||
|
||||
|
@ -255,23 +253,46 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
return item;
|
||||
}
|
||||
|
||||
private async Task<LiveTvRecording> GetRecording(RecordingInfo info, string serviceName, CancellationToken cancellationToken)
|
||||
private async Task<ILiveTvRecording> GetRecording(RecordingInfo info, string serviceName, CancellationToken cancellationToken)
|
||||
{
|
||||
var isNew = false;
|
||||
|
||||
var id = _tvDtoService.GetInternalRecordingId(serviceName, info.Id);
|
||||
|
||||
var item = _itemRepo.RetrieveItem(id) as LiveTvRecording;
|
||||
var item = _itemRepo.RetrieveItem(id) as ILiveTvRecording;
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
item = new LiveTvRecording
|
||||
if (info.ChannelType == ChannelType.TV)
|
||||
{
|
||||
Name = info.Name,
|
||||
Id = id,
|
||||
DateCreated = DateTime.UtcNow,
|
||||
DateModified = DateTime.UtcNow
|
||||
};
|
||||
item = new LiveTvVideoRecording
|
||||
{
|
||||
Name = info.Name,
|
||||
Id = id,
|
||||
DateCreated = DateTime.UtcNow,
|
||||
DateModified = DateTime.UtcNow,
|
||||
VideoType = VideoType.VideoFile
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
item = new LiveTvAudioRecording
|
||||
{
|
||||
Name = info.Name,
|
||||
Id = id,
|
||||
DateCreated = DateTime.UtcNow,
|
||||
DateModified = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(info.Path))
|
||||
{
|
||||
item.Path = info.Path;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(info.Url))
|
||||
{
|
||||
item.Path = info.Url;
|
||||
}
|
||||
|
||||
isNew = true;
|
||||
}
|
||||
|
@ -331,7 +352,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
|
||||
if (user != null)
|
||||
{
|
||||
programs = programs.Where(i => i.IsParentalAllowed(user, _localization));
|
||||
programs = programs.Where(i => i.IsParentalAllowed(user));
|
||||
}
|
||||
|
||||
var returnArray = programs
|
||||
|
@ -450,10 +471,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
|
||||
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(new Guid(query.UserId));
|
||||
|
||||
var list = new List<RecordingInfo>();
|
||||
|
||||
var recordings = await service.GetRecordingsAsync(cancellationToken).ConfigureAwait(false);
|
||||
list.AddRange(recordings);
|
||||
|
||||
if (!string.IsNullOrEmpty(query.ChannelId))
|
||||
{
|
||||
|
@ -461,9 +479,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
|
||||
var currentServiceName = service.Name;
|
||||
|
||||
list = list
|
||||
.Where(i => _tvDtoService.GetInternalChannelId(currentServiceName, i.ChannelId) == guid)
|
||||
.ToList();
|
||||
recordings = recordings
|
||||
.Where(i => _tvDtoService.GetInternalChannelId(currentServiceName, i.ChannelId) == guid);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(query.Id))
|
||||
|
@ -472,27 +489,31 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
|
||||
var currentServiceName = service.Name;
|
||||
|
||||
list = list
|
||||
.Where(i => _tvDtoService.GetInternalRecordingId(currentServiceName, i.Id) == guid)
|
||||
.ToList();
|
||||
recordings = recordings
|
||||
.Where(i => _tvDtoService.GetInternalRecordingId(currentServiceName, i.Id) == guid);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(query.GroupId))
|
||||
{
|
||||
var guid = new Guid(query.GroupId);
|
||||
|
||||
list = list.Where(i => GetRecordingGroupIds(i).Contains(guid))
|
||||
.ToList();
|
||||
recordings = recordings.Where(i => GetRecordingGroupIds(i).Contains(guid));
|
||||
}
|
||||
|
||||
IEnumerable<LiveTvRecording> entities = await GetEntities(list, service.Name, cancellationToken).ConfigureAwait(false);
|
||||
if (query.IsRecording.HasValue)
|
||||
{
|
||||
var val = query.IsRecording.Value;
|
||||
recordings = recordings.Where(i => (i.Status == RecordingStatus.InProgress) == val);
|
||||
}
|
||||
|
||||
IEnumerable<ILiveTvRecording> entities = await GetEntities(recordings, service.Name, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
entities = entities.OrderByDescending(i => i.RecordingInfo.StartDate);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
var currentUser = user;
|
||||
entities = entities.Where(i => i.IsParentalAllowed(currentUser, _localization));
|
||||
entities = entities.Where(i => i.IsParentalAllowed(currentUser));
|
||||
}
|
||||
|
||||
if (query.StartIndex.HasValue)
|
||||
|
@ -520,7 +541,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
};
|
||||
}
|
||||
|
||||
private Task<LiveTvRecording[]> GetEntities(IEnumerable<RecordingInfo> recordings, string serviceName, CancellationToken cancellationToken)
|
||||
private Task<ILiveTvRecording[]> GetEntities(IEnumerable<RecordingInfo> recordings, string serviceName, CancellationToken cancellationToken)
|
||||
{
|
||||
var tasks = recordings.Select(i => GetRecording(i, serviceName, cancellationToken));
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
|
||||
public override bool Supports(BaseItem item)
|
||||
{
|
||||
return item is LiveTvRecording;
|
||||
return item is ILiveTvRecording;
|
||||
}
|
||||
|
||||
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
|
||||
|
@ -55,7 +55,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
|
||||
try
|
||||
{
|
||||
changed = await DownloadImage((LiveTvRecording)item, cancellationToken).ConfigureAwait(false);
|
||||
changed = await DownloadImage((ILiveTvRecording)item, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
|
@ -74,7 +74,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
return changed;
|
||||
}
|
||||
|
||||
private async Task<bool> DownloadImage(LiveTvRecording item, CancellationToken cancellationToken)
|
||||
private async Task<bool> DownloadImage(ILiveTvRecording item, CancellationToken cancellationToken)
|
||||
{
|
||||
var recordingInfo = item.RecordingInfo;
|
||||
|
||||
|
@ -133,7 +133,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||
// Dummy up the original url
|
||||
var url = item.ServiceName + recordingInfo.Id;
|
||||
|
||||
await _providerManager.SaveImage(item, imageStream, contentType, ImageType.Primary, null, url, cancellationToken).ConfigureAwait(false);
|
||||
await _providerManager.SaveImage((BaseItem)item, imageStream, contentType, ImageType.Primary, null, url, cancellationToken).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -100,7 +100,8 @@
|
|||
<Compile Include="Configuration\ServerConfigurationManager.cs" />
|
||||
<Compile Include="Drawing\ImageHeader.cs" />
|
||||
<Compile Include="Drawing\PercentPlayedDrawer.cs" />
|
||||
<Compile Include="Drawing\WatchedIndicatorDrawer.cs" />
|
||||
<Compile Include="Drawing\PlayedIndicatorDrawer.cs" />
|
||||
<Compile Include="Drawing\UnplayedCountIndicator.cs" />
|
||||
<Compile Include="Dto\DtoService.cs" />
|
||||
<Compile Include="EntryPoints\LibraryChangedNotifier.cs" />
|
||||
<Compile Include="EntryPoints\LoadRegistrations.cs" />
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace MediaBrowser.Server.Implementations.Providers
|
|||
throw new ArgumentNullException("mimeType");
|
||||
}
|
||||
|
||||
var saveLocally = _config.Configuration.SaveLocalMeta && item.Parent != null && !(item is Audio);
|
||||
var saveLocally = item.IsSaveLocalMetadataEnabled() && item.Parent != null && !(item is Audio);
|
||||
|
||||
if (item is IItemByName || item is User)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using MediaBrowser.Common.Implementations.Networking;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Net;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -26,9 +27,9 @@ namespace MediaBrowser.ServerApplication.Networking
|
|||
/// Gets a list of network devices
|
||||
/// </summary>
|
||||
/// PC's in the Domain</returns>
|
||||
public IEnumerable<string> GetNetworkDevices()
|
||||
public IEnumerable<FileSystemEntryInfo> GetNetworkDevices()
|
||||
{
|
||||
return new List<string> ();
|
||||
return new List<FileSystemEntryInfo> ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -272,7 +272,7 @@ namespace MediaBrowser.ServerApplication
|
|||
DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor);
|
||||
RegisterSingleInstance(DtoService);
|
||||
|
||||
LiveTvManager = new LiveTvManager(ApplicationPaths, FileSystemManager, Logger, ItemRepository, ImageProcessor, LocalizationManager, UserDataManager, DtoService, UserManager);
|
||||
LiveTvManager = new LiveTvManager(ApplicationPaths, FileSystemManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager);
|
||||
RegisterSingleInstance(LiveTvManager);
|
||||
progress.Report(15);
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
using MediaBrowser.Common.Implementations.Networking;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using MediaBrowser.Common.Implementations.Networking;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Net;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -79,7 +82,7 @@ namespace MediaBrowser.ServerApplication.Networking
|
|||
/// </summary>
|
||||
/// <returns>Arraylist that represents all the SV_TYPE_WORKSTATION and SV_TYPE_SERVER
|
||||
/// PC's in the Domain</returns>
|
||||
public IEnumerable<string> GetNetworkDevices()
|
||||
private IEnumerable<string> GetNetworkDevicesInternal()
|
||||
{
|
||||
//local fields
|
||||
const int MAX_PREFERRED_LENGTH = -1;
|
||||
|
@ -131,6 +134,33 @@ namespace MediaBrowser.ServerApplication.Networking
|
|||
NativeMethods.NetApiBufferFree(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets available devices within the domain
|
||||
/// </summary>
|
||||
/// <returns>PC's in the Domain</returns>
|
||||
public IEnumerable<FileSystemEntryInfo> GetNetworkDevices()
|
||||
{
|
||||
return GetNetworkDevicesInternal().Select(c => new FileSystemEntryInfo
|
||||
{
|
||||
Name = c,
|
||||
Path = NetworkPrefix + c,
|
||||
Type = FileSystemEntryType.NetworkComputer
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the network prefix.
|
||||
/// </summary>
|
||||
/// <value>The network prefix.</value>
|
||||
private string NetworkPrefix
|
||||
{
|
||||
get
|
||||
{
|
||||
var separator = Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture);
|
||||
return separator + separator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -498,6 +498,7 @@ namespace MediaBrowser.WebDashboard.Api
|
|||
"livetvnewrecording.js",
|
||||
"livetvprogram.js",
|
||||
"livetvrecording.js",
|
||||
"livetvrecordinglist.js",
|
||||
"livetvrecordings.js",
|
||||
"livetvtimer.js",
|
||||
"livetvseriestimer.js",
|
||||
|
|
|
@ -872,6 +872,47 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets shares from a network device
|
||||
*/
|
||||
self.getNetworkShares = function (path) {
|
||||
|
||||
if (!path) {
|
||||
throw new Error("null path");
|
||||
}
|
||||
|
||||
var options = {};
|
||||
options.path = path;
|
||||
|
||||
var url = self.getUrl("Environment/NetworkShares", options);
|
||||
|
||||
return self.ajax({
|
||||
type: "GET",
|
||||
url: url,
|
||||
dataType: "json"
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the parent of a given path
|
||||
*/
|
||||
self.getParentPath = function (path) {
|
||||
|
||||
if (!path) {
|
||||
throw new Error("null path");
|
||||
}
|
||||
|
||||
var options = {};
|
||||
options.path = path;
|
||||
|
||||
var url = self.getUrl("Environment/ParentPath", options);
|
||||
|
||||
return self.ajax({
|
||||
type: "GET",
|
||||
url: url
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a list of physical drives from the server
|
||||
*/
|
||||
|
|
|
@ -154,6 +154,9 @@
|
|||
<Content Include="dashboard-ui\livetvrecording.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\livetvrecordinglist.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\livetvseriestimer.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
@ -424,6 +427,9 @@
|
|||
<Content Include="dashboard-ui\scripts\livetvrecording.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\scripts\livetvrecordinglist.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\scripts\livetvseriestimer.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.213" targetFramework="net45" />
|
||||
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.216" targetFramework="net45" />
|
||||
</packages>
|
Loading…
Reference in New Issue
Block a user