mirror of
https://github.com/Radarr/Radarr.git
synced 2026-03-30 18:25:57 -04:00
Compare commits
10 Commits
v2.0.0.492
...
v2.0.0.494
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35f911286f | ||
|
|
650f01176c | ||
|
|
2246dfab05 | ||
|
|
6bfb790eb8 | ||
|
|
0e85e39815 | ||
|
|
9471343533 | ||
|
|
2d1d1c8a99 | ||
|
|
7e5e136930 | ||
|
|
c659ba1c10 | ||
|
|
caf7a8c69e |
@@ -139,11 +139,26 @@ namespace NzbDrone.Common.Test.Http
|
||||
var request = new HttpRequest($"http://{_httpBinHost}/redirect/1");
|
||||
request.AllowAutoRedirect = true;
|
||||
|
||||
Subject.Get(request);
|
||||
var response = Subject.Get(request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
ExceptionVerification.ExpectedErrors(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_follow_redirects()
|
||||
{
|
||||
var request = new HttpRequest($"http://{_httpBinHost}/redirect/1");
|
||||
request.AllowAutoRedirect = false;
|
||||
|
||||
var response = Subject.Get(request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Found);
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_follow_redirects_to_https()
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
[TestCase(@"https://rss.omgwtfnzbs.org/rss-search.php?catid=19,20&user=sonarr&api=mySecret&eng=1")]
|
||||
[TestCase(@"https://dognzb.cr/fetch/2b51db35e1912ffc138825a12b9933d2/2b51db35e1910123321025a12b9933d2")]
|
||||
[TestCase(@"https://baconbits.org/feeds.php?feed=torrents_tv&user=12345&auth=2b51db35e1910123321025a12b9933d2&passkey=mySecret&authkey=2b51db35e1910123321025a12b9933d2")]
|
||||
[TestCase(@"http://127.0.0.1:9117/dl/indexername?jackett_apikey=flwjiefewklfjacketmySecretsdfldskjfsdlk&path=we0re9f0sdfbase64sfdkfjsdlfjk&file=The+Torrent+File+Name.torrent")]
|
||||
// NzbGet
|
||||
[TestCase(@"{ ""Name"" : ""ControlUsername"", ""Value"" : ""mySecret"" }, { ""Name"" : ""ControlPassword"", ""Value"" : ""mySecret"" }, ")]
|
||||
[TestCase(@"{ ""Name"" : ""Server1.Username"", ""Value"" : ""mySecret"" }, { ""Name"" : ""Server1.Password"", ""Value"" : ""mySecret"" }, ")]
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace NzbDrone.Common.Disk
|
||||
{
|
||||
get
|
||||
{
|
||||
if (VolumeLabel.IsNullOrWhiteSpace())
|
||||
if (VolumeLabel.IsNullOrWhiteSpace() || VolumeLabel.StartsWith("UUID=") || Name == VolumeLabel)
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
@@ -52,36 +52,33 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
public HttpResponse Execute(HttpRequest request)
|
||||
{
|
||||
var autoRedirectCount = 0;
|
||||
var autoRedirectChain = new List<string>();
|
||||
autoRedirectChain.Add(request.Url.ToString());
|
||||
|
||||
var response = ExecuteRequest(request);
|
||||
|
||||
while (response.StatusCode == HttpStatusCode.Moved ||
|
||||
response.StatusCode == HttpStatusCode.MovedPermanently ||
|
||||
response.StatusCode == HttpStatusCode.Found)
|
||||
if (request.AllowAutoRedirect && response.HasHttpRedirect)
|
||||
{
|
||||
if (request.AllowAutoRedirect)
|
||||
var autoRedirectChain = new List<string>();
|
||||
autoRedirectChain.Add(request.Url.ToString());
|
||||
|
||||
do
|
||||
{
|
||||
request.Url += new HttpUri(response.Headers.GetSingleValue("Location"));
|
||||
autoRedirectChain.Add(request.Url.ToString());
|
||||
|
||||
_logger.Trace("Redirected to {0}", request.Url);
|
||||
|
||||
autoRedirectCount++;
|
||||
if (autoRedirectCount > 3)
|
||||
if (autoRedirectChain.Count > 3)
|
||||
{
|
||||
throw new WebException($"Too many automatic redirections were attempted for {autoRedirectChain.Join(" -> ")}", WebExceptionStatus.ProtocolError);
|
||||
}
|
||||
|
||||
response = ExecuteRequest(request);
|
||||
}
|
||||
else if (!RuntimeInfo.IsProduction)
|
||||
{
|
||||
_logger.Error("Server requested a redirect to [{0}]. Update the request URL to avoid this redirect.", response.Headers["Location"]);
|
||||
break;
|
||||
}
|
||||
while (response.HasHttpRedirect);
|
||||
}
|
||||
|
||||
if (response.HasHttpRedirect && !RuntimeInfo.IsProduction)
|
||||
{
|
||||
_logger.Error("Server requested a redirect to [{0}] while in developer mode. Update the request URL to avoid this redirect.", response.Headers["Location"]);
|
||||
}
|
||||
|
||||
if (!request.SuppressHttpError && response.HasHttpError)
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
private string _content;
|
||||
|
||||
public string Content
|
||||
public string Content
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -51,6 +51,10 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
public bool HasHttpError => (int)StatusCode >= 400;
|
||||
|
||||
public bool HasHttpRedirect => StatusCode == HttpStatusCode.Moved ||
|
||||
StatusCode == HttpStatusCode.MovedPermanently ||
|
||||
StatusCode == HttpStatusCode.Found;
|
||||
|
||||
public Dictionary<string, string> GetCookies()
|
||||
{
|
||||
var result = new Dictionary<string, string>();
|
||||
@@ -95,4 +99,4 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
public T Resource { get; private set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ namespace NzbDrone.Common.Instrumentation
|
||||
{
|
||||
public class CleanseLogMessage
|
||||
{
|
||||
private static readonly Regex[] CleansingRules = new[]
|
||||
private static readonly Regex[] CleansingRules = new[]
|
||||
{
|
||||
// Url
|
||||
new Regex(@"(?<=\?|&)(apikey|token|passkey|auth|authkey|user|uid|api)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?<=\?|&)(apikey|token|passkey|auth|authkey|user|uid|api|[a-z_]*apikey)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?<=\?|&)[^=]*?(username|password)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"torrentleech\.org/(?!rss)(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"torrentleech\.org/rss/download/[0-9]+/(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
@@ -130,5 +130,26 @@ namespace NzbDrone.Core.Test.DiskSpace
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.GetAvailableSpace(It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[TestCase("/boot")]
|
||||
[TestCase("/var/lib/rancher")]
|
||||
[TestCase("/var/lib/rancher/volumes")]
|
||||
[TestCase("/var/lib/kubelet")]
|
||||
[TestCase("/var/lib/docker")]
|
||||
[TestCase("/some/place/docker/aufs")]
|
||||
public void should_not_check_diskspace_for_irrelevant_mounts(string path)
|
||||
{
|
||||
var mount = new Mock<IMount>();
|
||||
mount.SetupGet(v => v.RootDirectory).Returns(path);
|
||||
mount.SetupGet(v => v.DriveType).Returns(System.IO.DriveType.Fixed);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.GetMounts())
|
||||
.Returns(new List<IMount> { mount.Object });
|
||||
|
||||
var freeSpace = Subject.GetFreeSpace();
|
||||
|
||||
freeSpace.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
{
|
||||
var mock = WithUsenetClient();
|
||||
mock.Setup(s => s.Download(It.IsAny<RemoteEpisode>()));
|
||||
|
||||
|
||||
Subject.DownloadReport(_parseResult);
|
||||
|
||||
VerifyEventPublished<EpisodeGrabbedEvent>();
|
||||
@@ -93,7 +93,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
{
|
||||
var mock = WithUsenetClient();
|
||||
mock.Setup(s => s.Download(It.IsAny<RemoteEpisode>()));
|
||||
|
||||
|
||||
Subject.DownloadReport(_parseResult);
|
||||
|
||||
mock.Verify(s => s.Download(It.IsAny<RemoteEpisode>()), Times.Once());
|
||||
@@ -117,7 +117,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
var mock = WithUsenetClient();
|
||||
mock.Setup(s => s.Download(It.IsAny<RemoteEpisode>()))
|
||||
.Callback<RemoteEpisode>(v => {
|
||||
throw new ReleaseDownloadException(v.Release, "Error", new WebException());
|
||||
throw new ReleaseDownloadException(v.Release, "Error", new WebException());
|
||||
});
|
||||
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.DownloadReport(_parseResult));
|
||||
@@ -136,7 +136,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
var mock = WithUsenetClient();
|
||||
mock.Setup(s => s.Download(It.IsAny<RemoteEpisode>()))
|
||||
.Callback<RemoteEpisode>(v => {
|
||||
throw new ReleaseDownloadException(v.Release, "Error", new TooManyRequestsException(request, response));
|
||||
throw new ReleaseDownloadException(v.Release, "Error", new TooManyRequestsException(request, response));
|
||||
});
|
||||
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.DownloadReport(_parseResult));
|
||||
@@ -182,12 +182,10 @@ namespace NzbDrone.Core.Test.Download
|
||||
[Test]
|
||||
public void should_not_attempt_download_if_client_isnt_configure()
|
||||
{
|
||||
Subject.DownloadReport(_parseResult);
|
||||
Assert.Throws<DownloadClientUnavailableException>(() => Subject.DownloadReport(_parseResult));
|
||||
|
||||
Mocker.GetMock<IDownloadClient>().Verify(c => c.Download(It.IsAny<RemoteEpisode>()), Times.Never());
|
||||
VerifyEventNotPublished<EpisodeGrabbedEvent>();
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -22,6 +23,8 @@ namespace NzbDrone.Core.DiskSpace
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private static readonly Regex _regexSpecialDrive = new Regex("^/var/lib/(docker|rancher|kubelet)(/|$)|^/boot(/|$)|/docker(/var)?/aufs(/|$)", RegexOptions.Compiled);
|
||||
|
||||
public DiskSpaceService(ISeriesService seriesService, IConfigService configService, IDiskProvider diskProvider, Logger logger)
|
||||
{
|
||||
_seriesService = seriesService;
|
||||
@@ -32,37 +35,37 @@ namespace NzbDrone.Core.DiskSpace
|
||||
|
||||
public List<DiskSpace> GetFreeSpace()
|
||||
{
|
||||
var diskSpace = new List<DiskSpace>();
|
||||
diskSpace.AddRange(GetSeriesFreeSpace());
|
||||
diskSpace.AddRange(GetDroneFactoryFreeSpace());
|
||||
diskSpace.AddRange(GetFixedDisksFreeSpace());
|
||||
var importantRootFolders = GetSeriesRootPaths().Concat(GetDroneFactoryRootPaths()).Distinct().ToList();
|
||||
|
||||
return diskSpace.DistinctBy(d => d.Path).ToList();
|
||||
var optionalRootFolders = GetFixedDisksRootPaths().Except(importantRootFolders).Distinct().ToList();
|
||||
|
||||
var diskSpace = GetDiskSpace(importantRootFolders).Concat(GetDiskSpace(optionalRootFolders, true)).ToList();
|
||||
|
||||
return diskSpace;
|
||||
}
|
||||
|
||||
private IEnumerable<DiskSpace> GetSeriesFreeSpace()
|
||||
private IEnumerable<string> GetSeriesRootPaths()
|
||||
{
|
||||
var seriesRootPaths = _seriesService.GetAllSeries()
|
||||
return _seriesService.GetAllSeries()
|
||||
.Where(s => _diskProvider.FolderExists(s.Path))
|
||||
.Select(s => _diskProvider.GetPathRoot(s.Path))
|
||||
.Distinct();
|
||||
|
||||
return GetDiskSpace(seriesRootPaths);
|
||||
}
|
||||
|
||||
private IEnumerable<DiskSpace> GetDroneFactoryFreeSpace()
|
||||
private IEnumerable<string> GetDroneFactoryRootPaths()
|
||||
{
|
||||
if (_configService.DownloadedEpisodesFolder.IsNotNullOrWhiteSpace() && _diskProvider.FolderExists(_configService.DownloadedEpisodesFolder))
|
||||
{
|
||||
return GetDiskSpace(new[] { _diskProvider.GetPathRoot(_configService.DownloadedEpisodesFolder) });
|
||||
yield return _configService.DownloadedEpisodesFolder;
|
||||
}
|
||||
|
||||
return new List<DiskSpace>();
|
||||
}
|
||||
|
||||
private IEnumerable<DiskSpace> GetFixedDisksFreeSpace()
|
||||
private IEnumerable<string> GetFixedDisksRootPaths()
|
||||
{
|
||||
return GetDiskSpace(_diskProvider.GetMounts().Where(d => d.DriveType == DriveType.Fixed).Select(d => d.RootDirectory), true);
|
||||
return _diskProvider.GetMounts()
|
||||
.Where(d => d.DriveType == DriveType.Fixed)
|
||||
.Where(d => !_regexSpecialDrive.IsMatch(d.RootDirectory))
|
||||
.Select(d => d.RootDirectory);
|
||||
}
|
||||
|
||||
private IEnumerable<DiskSpace> GetDiskSpace(IEnumerable<string> paths, bool suppressWarnings = false)
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
{
|
||||
case "error": // some error occurred, applies to paused torrents
|
||||
item.Status = DownloadItemStatus.Failed;
|
||||
item.Message = "QBittorrent is reporting an error";
|
||||
item.Message = "qBittorrent is reporting an error";
|
||||
break;
|
||||
|
||||
case "pausedDL": // torrent is paused and has NOT finished downloading
|
||||
@@ -222,7 +222,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
var config = _proxy.GetConfig(Settings);
|
||||
if (config.MaxRatioEnabled && config.RemoveOnMaxRatio)
|
||||
{
|
||||
return new NzbDroneValidationFailure(String.Empty, "QBittorrent is configured to remove torrents when they reach their Share Ratio Limit")
|
||||
return new NzbDroneValidationFailure(String.Empty, "qBittorrent is configured to remove torrents when they reach their Share Ratio Limit")
|
||||
{
|
||||
DetailedDescription = "Sonarr will be unable to perform Completed Download Handling as configured. You can fix this in qBittorrent ('Tools -> Options...' in the menu) by changing 'Options -> BitTorrent -> Share Ratio Limiting' from 'Remove them' to 'Pause them'."
|
||||
};
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
}
|
||||
catch(DownloadClientException ex)
|
||||
{
|
||||
// if setCategory fails due to method not being found, then try older setLabel command for qbittorent < v.3.3.5
|
||||
// if setCategory fails due to method not being found, then try older setLabel command for qBittorrent < v.3.3.5
|
||||
if (ex.InnerException is HttpException && (ex.InnerException as HttpException).Response.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
var setLabelRequest = BuildRequest(settings).Resource("/command/setLabel")
|
||||
@@ -197,12 +197,12 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new DownloadClientException("Failed to connect to qBitTorrent, check your settings.", ex);
|
||||
throw new DownloadClientException("Failed to connect to qBittorrent, check your settings.", ex);
|
||||
}
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
throw new DownloadClientException("Failed to connect to qBitTorrent, please check your settings.", ex);
|
||||
throw new DownloadClientException("Failed to connect to qBittorrent, please check your settings.", ex);
|
||||
}
|
||||
|
||||
return response.Content;
|
||||
@@ -239,23 +239,23 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
_logger.Debug("qbitTorrent authentication failed.");
|
||||
if (ex.Response.StatusCode == HttpStatusCode.Forbidden)
|
||||
{
|
||||
throw new DownloadClientAuthenticationException("Failed to authenticate with qbitTorrent.", ex);
|
||||
throw new DownloadClientAuthenticationException("Failed to authenticate with qBittorrent.", ex);
|
||||
}
|
||||
|
||||
throw new DownloadClientException("Failed to connect to qBitTorrent, please check your settings.", ex);
|
||||
throw new DownloadClientException("Failed to connect to qBittorrent, please check your settings.", ex);
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
throw new DownloadClientUnavailableException("Failed to connect to qBitTorrent, please check your settings.", ex);
|
||||
throw new DownloadClientUnavailableException("Failed to connect to qBittorrent, please check your settings.", ex);
|
||||
}
|
||||
|
||||
if (response.Content != "Ok.") // returns "Fails." on bad login
|
||||
{
|
||||
_logger.Debug("qbitTorrent authentication failed.");
|
||||
throw new DownloadClientAuthenticationException("Failed to authenticate with qbitTorrent.");
|
||||
throw new DownloadClientAuthenticationException("Failed to authenticate with qBittorrent.");
|
||||
}
|
||||
|
||||
_logger.Debug("qbitTorrent authentication succeeded.");
|
||||
_logger.Debug("qBittorrent authentication succeeded.");
|
||||
|
||||
cookies = response.GetCookies();
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Common.TPL;
|
||||
using NzbDrone.Core.Download.Clients;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
@@ -52,8 +53,7 @@ namespace NzbDrone.Core.Download
|
||||
|
||||
if (downloadClient == null)
|
||||
{
|
||||
_logger.Warn("{0} Download client isn't configured yet.", remoteEpisode.Release.DownloadProtocol);
|
||||
return;
|
||||
throw new DownloadClientUnavailableException($"{remoteEpisode.Release.DownloadProtocol} Download client isn't configured yet");
|
||||
}
|
||||
|
||||
// Limit grabs to 2 per second.
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace NzbDrone.Core.IndexerSearch.Definitions
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[{0} : {1:yyyy-MM-dd}", Series.Title, AirDate);
|
||||
return string.Format("[{0} : {1:yyyy-MM-dd}]", Series.Title, AirDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,24 +40,15 @@ namespace NzbDrone.Mono.Disk
|
||||
{
|
||||
Ensure.That(path, () => path).IsValidPath();
|
||||
|
||||
try
|
||||
{
|
||||
var mount = GetMount(path);
|
||||
var mount = GetMount(path);
|
||||
|
||||
if (mount == null)
|
||||
{
|
||||
Logger.Debug("Unable to get free space for '{0}', unable to find suitable drive", path);
|
||||
return null;
|
||||
}
|
||||
|
||||
return mount.AvailableFreeSpace;
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
if (mount == null)
|
||||
{
|
||||
Logger.Error(ex, "Couldn't get free space for {0}", path);
|
||||
Logger.Debug("Unable to get free space for '{0}', unable to find suitable drive", path);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
return mount.AvailableFreeSpace;
|
||||
}
|
||||
|
||||
public override void InheritFolderPermissions(string filename)
|
||||
@@ -100,20 +91,9 @@ namespace NzbDrone.Mono.Disk
|
||||
{
|
||||
Ensure.That(path, () => path).IsValidPath();
|
||||
|
||||
try
|
||||
{
|
||||
var mount = GetMount(path);
|
||||
var mount = GetMount(path);
|
||||
|
||||
if (mount == null) return null;
|
||||
|
||||
return mount.TotalSize;
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
Logger.Error(e, "Couldn't get total space for {0}", path);
|
||||
}
|
||||
|
||||
return null;
|
||||
return mount?.TotalSize;
|
||||
}
|
||||
|
||||
public override bool TryCreateHardLink(string source, string destination)
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace NzbDrone.Mono.Disk
|
||||
{
|
||||
get
|
||||
{
|
||||
if (VolumeLabel.IsNullOrWhiteSpace() || VolumeLabel.StartsWith("UUID="))
|
||||
if (VolumeLabel.IsNullOrWhiteSpace() || VolumeLabel.StartsWith("UUID=") || Name == VolumeLabel)
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ module.exports = Backgrid.Cell.extend({
|
||||
|
||||
var contents = path;
|
||||
|
||||
if (label) {
|
||||
if (label && label !== path && !label.startsWith("UUID=")) {
|
||||
contents += ' ({0})'.format(label);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user