add more methods to file system interface

pull/702/head
Luke Pulverenti 11 years ago
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
}
}
}
}
/// <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)
public bool ContainsSubPath(string parentPath, string path)
{
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
if (string.IsNullOrEmpty(parentPath))
{
IsDirectory = false;
throw new ArgumentNullException("parentPath");
}
// 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)
if (string.IsNullOrEmpty(path))
{
// the plus 2 accounts for the length marker itself
shell_len = bytesToWord(link, shell_offset) + 2;
throw new ArgumentNullException("path");
}
return path.IndexOf(parentPath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) != -1;
}
// 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)
public bool IsRootPath(string path)
{
if (string.IsNullOrEmpty(path))
{
int basename_offset = link[file_start + basename_offset_offset] + file_start;
String basename = getNullDelimitedString(link, basename_offset, encoding);
ResolvedPath = basename + finalname;
throw new ArgumentNullException("path");
}
else
var parent = Path.GetDirectoryName(path);
if (!string.IsNullOrEmpty(parent))
{
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;
return false;
}
return true;
}
private static string getNullDelimitedString(byte[] bytes, int off, Encoding encoding)
public string NormalizePath(string path)
{
int len = 0;
// count bytes until the null character (0)
while (true)
if (string.IsNullOrEmpty(path))
{
if (bytes[off + len] == 0)
{
break;
}
len++;
throw new ArgumentNullException("path");
}
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);
}
if (path.EndsWith(":\\", StringComparison.OrdinalIgnoreCase))
{
return path;
}
private static int bytesToDword(byte[] bytes, int off)
{
return (bytesToWord(bytes, off + 2) << 16) | bytesToWord(bytes, off);
return path.TrimEnd(Path.DirectorySeparatorChar);
}
}
}

@ -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,85 +519,84 @@ 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);
}
tasks.Add(RefreshChild(tuple, progress, percentages, list.Count, cancellationToken, recursive, forceRefreshMetadata));
}
// 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));
cancellationToken.ThrowIfCancellationRequested();
if (refreshChildren)
{
// Don't refresh children if explicitly set to false
if (recursive.HasValue && recursive.Value == false)
{
refreshChildren = false;
}
}
await Task.WhenAll(tasks).ConfigureAwait(false);
}
if (refreshChildren)
{
cancellationToken.ThrowIfCancellationRequested();
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 innerProgress = new ActionableProgress<double>();
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);
}
innerProgress.RegisterAction(p =>
{
lock (percentages)
{
percentages[child.Id] = p / 100;
// 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));
var percent = percentages.Values.Sum();
percent /= list.Count;
if (refreshChildren)
{
// Don't refresh children if explicitly set to false
if (recursive.HasValue && recursive.Value == false)
{
refreshChildren = false;
}
}
progress.Report((90 * percent) + 10);
}
});
if (refreshChildren)
{
cancellationToken.ThrowIfCancellationRequested();
await ((Folder)child).ValidateChildren(innerProgress, cancellationToken, recursive, forceRefreshMetadata).ConfigureAwait(false);
var innerProgress = new ActionableProgress<double>();
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
innerProgress.RegisterAction(p =>
{
lock (percentages)
{
lock (percentages)
{
percentages[child.Id] = 1;
percentages[child.Id] = p / 100;
var percent = percentages.Values.Sum();
percent /= list.Count;
var percent = percentages.Values.Sum();
percent /= childCount;
progress.Report((90 * percent) + 10);
}
progress.Report((90 * percent) + 10);
}
});
}, cancellationToken));
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;
cancellationToken.ThrowIfCancellationRequested();
var percent = percentages.Values.Sum();
percent /= childCount;
await Task.WhenAll(tasks).ConfigureAwait(false);
progress.Report((90 * percent) + 10);
}
}
}
/// <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.

@ -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);
}
}

@ -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);
new PlayedIndicatorDrawer().DrawPlayedIndicator(graphics, currentImageSize);
}
else if (options.UnplayedCount.HasValue)
{
var currentImageSize = new Size(imageWidth, imageHeight);
percentOffset = 0 - WatchedIndicatorDrawer.IndicatorWidth;
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));
}
if (query.IsRecording.HasValue)
{
var val = query.IsRecording.Value;
recordings = recordings.Where(i => (i.Status == RecordingStatus.InProgress) == val);
}
IEnumerable<LiveTvRecording> entities = await GetEntities(list, service.Name, cancellationToken).ConfigureAwait(false);
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…
Cancel
Save