Browse Source

fix path replacement bug (#677)

pull/678/head
Jason Dove 4 years ago committed by GitHub
parent
commit
fc1a051df5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 271
      ErsatzTV.Core.Tests/Emby/EmbyPathReplacementServiceTests.cs
  3. 64
      ErsatzTV.Core.Tests/Jellyfin/JellyfinPathReplacementServiceTests.cs
  4. 64
      ErsatzTV.Core.Tests/Plex/PlexPathReplacementServiceTests.cs
  5. 13
      ErsatzTV.Core/Emby/EmbyPathReplacementService.cs
  6. 13
      ErsatzTV.Core/Jellyfin/JellyfinPathReplacementService.cs
  7. 13
      ErsatzTV.Core/Plex/PlexPathReplacementService.cs

1
CHANGELOG.md

@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix bug scheduling mid-roll filler with content that contains one chapter - Fix bug scheduling mid-roll filler with content that contains one chapter
- No mid-roll filler will be inserted for content with zero or one chapters - No mid-roll filler will be inserted for content with zero or one chapters
- Fix thread sync bug with `HLS Segmenter` (and `MPEG-TS`) streaming modes - Fix thread sync bug with `HLS Segmenter` (and `MPEG-TS`) streaming modes
- Fix path replacement bug when media server path is left blank
### Added ### Added
- Add automated error reporting via Bugsnag - Add automated error reporting via Bugsnag

271
ErsatzTV.Core.Tests/Emby/EmbyPathReplacementServiceTests.cs

@ -0,0 +1,271 @@
using System.Runtime.InteropServices;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Interfaces.Runtime;
using ErsatzTV.Core.Emby;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
using NUnit.Framework;
namespace ErsatzTV.Core.Tests.Emby;
[TestFixture]
public class EmbyPathReplacementServiceTests
{
[Test]
public async Task EmbyWindows_To_EtvWindows()
{
var replacements = new List<EmbyPathReplacement>
{
new()
{
Id = 1,
EmbyPath = @"C:\Something\Some Shared Folder",
LocalPath = @"C:\Something Else\Some Shared Folder",
EmbyMediaSource = new EmbyMediaSource { OperatingSystem = "Windows" }
}
};
var repo = new Mock<IMediaSourceRepository>();
repo.Setup(x => x.GetEmbyPathReplacementsByLibraryId(It.IsAny<int>())).Returns(replacements.AsTask());
var runtime = new Mock<IRuntimeInfo>();
runtime.Setup(x => x.IsOSPlatform(OSPlatform.Windows)).Returns(true);
var service = new EmbyPathReplacementService(
repo.Object,
runtime.Object,
new Mock<ILogger<EmbyPathReplacementService>>().Object);
string result = await service.GetReplacementEmbyPath(
0,
@"C:\Something\Some Shared Folder\Some Movie\Some Movie.mkv");
result.Should().Be(@"C:\Something Else\Some Shared Folder\Some Movie\Some Movie.mkv");
}
[Test]
public async Task EmbyWindows_To_EtvLinux()
{
var replacements = new List<EmbyPathReplacement>
{
new()
{
Id = 1,
EmbyPath = @"C:\Something\Some Shared Folder",
LocalPath = @"/mnt/something else/Some Shared Folder",
EmbyMediaSource = new EmbyMediaSource { OperatingSystem = "Windows" }
}
};
var repo = new Mock<IMediaSourceRepository>();
repo.Setup(x => x.GetEmbyPathReplacementsByLibraryId(It.IsAny<int>())).Returns(replacements.AsTask());
var runtime = new Mock<IRuntimeInfo>();
runtime.Setup(x => x.IsOSPlatform(OSPlatform.Windows)).Returns(false);
var service = new EmbyPathReplacementService(
repo.Object,
runtime.Object,
new Mock<ILogger<EmbyPathReplacementService>>().Object);
string result = await service.GetReplacementEmbyPath(
0,
@"C:\Something\Some Shared Folder\Some Movie\Some Movie.mkv");
result.Should().Be(@"/mnt/something else/Some Shared Folder/Some Movie/Some Movie.mkv");
}
[Test]
public async Task EmbyWindows_To_EtvLinux_UncPath()
{
var replacements = new List<EmbyPathReplacement>
{
new()
{
Id = 1,
EmbyPath = @"\\192.168.1.100\Something\Some Shared Folder",
LocalPath = @"/mnt/something else/Some Shared Folder",
EmbyMediaSource = new EmbyMediaSource { OperatingSystem = "Windows" }
}
};
var repo = new Mock<IMediaSourceRepository>();
repo.Setup(x => x.GetEmbyPathReplacementsByLibraryId(It.IsAny<int>())).Returns(replacements.AsTask());
var runtime = new Mock<IRuntimeInfo>();
runtime.Setup(x => x.IsOSPlatform(OSPlatform.Windows)).Returns(false);
var service = new EmbyPathReplacementService(
repo.Object,
runtime.Object,
new Mock<ILogger<EmbyPathReplacementService>>().Object);
string result = await service.GetReplacementEmbyPath(
0,
@"\\192.168.1.100\Something\Some Shared Folder\Some Movie\Some Movie.mkv");
result.Should().Be(@"/mnt/something else/Some Shared Folder/Some Movie/Some Movie.mkv");
}
[Test]
public async Task EmbyWindows_To_EtvLinux_UncPathWithTrailingSlash()
{
var replacements = new List<EmbyPathReplacement>
{
new()
{
Id = 1,
EmbyPath = @"\\192.168.1.100\Something\Some Shared Folder\",
LocalPath = @"/mnt/something else/Some Shared Folder/",
EmbyMediaSource = new EmbyMediaSource { OperatingSystem = "Windows" }
}
};
var repo = new Mock<IMediaSourceRepository>();
repo.Setup(x => x.GetEmbyPathReplacementsByLibraryId(It.IsAny<int>())).Returns(replacements.AsTask());
var runtime = new Mock<IRuntimeInfo>();
runtime.Setup(x => x.IsOSPlatform(OSPlatform.Windows)).Returns(false);
var service = new EmbyPathReplacementService(
repo.Object,
runtime.Object,
new Mock<ILogger<EmbyPathReplacementService>>().Object);
string result = await service.GetReplacementEmbyPath(
0,
@"\\192.168.1.100\Something\Some Shared Folder\Some Movie\Some Movie.mkv");
result.Should().Be(@"/mnt/something else/Some Shared Folder/Some Movie/Some Movie.mkv");
}
[Test]
public async Task EmbyLinux_To_EtvWindows()
{
var replacements = new List<EmbyPathReplacement>
{
new()
{
Id = 1,
EmbyPath = @"/mnt/something/Some Shared Folder",
LocalPath = @"C:\Something Else\Some Shared Folder",
EmbyMediaSource = new EmbyMediaSource { OperatingSystem = "Linux" }
}
};
var repo = new Mock<IMediaSourceRepository>();
repo.Setup(x => x.GetEmbyPathReplacementsByLibraryId(It.IsAny<int>())).Returns(replacements.AsTask());
var runtime = new Mock<IRuntimeInfo>();
runtime.Setup(x => x.IsOSPlatform(OSPlatform.Windows)).Returns(true);
var service = new EmbyPathReplacementService(
repo.Object,
runtime.Object,
new Mock<ILogger<EmbyPathReplacementService>>().Object);
string result = await service.GetReplacementEmbyPath(
0,
@"/mnt/something/Some Shared Folder/Some Movie/Some Movie.mkv");
result.Should().Be(@"C:\Something Else\Some Shared Folder\Some Movie\Some Movie.mkv");
}
[Test]
public async Task EmbyLinux_To_EtvLinux()
{
var replacements = new List<EmbyPathReplacement>
{
new()
{
Id = 1,
EmbyPath = @"/mnt/something/Some Shared Folder",
LocalPath = @"/mnt/something else/Some Shared Folder",
EmbyMediaSource = new EmbyMediaSource { OperatingSystem = "Linux" }
}
};
var repo = new Mock<IMediaSourceRepository>();
repo.Setup(x => x.GetEmbyPathReplacementsByLibraryId(It.IsAny<int>())).Returns(replacements.AsTask());
var runtime = new Mock<IRuntimeInfo>();
runtime.Setup(x => x.IsOSPlatform(OSPlatform.Windows)).Returns(false);
var service = new EmbyPathReplacementService(
repo.Object,
runtime.Object,
new Mock<ILogger<EmbyPathReplacementService>>().Object);
string result = await service.GetReplacementEmbyPath(
0,
@"/mnt/something/Some Shared Folder/Some Movie/Some Movie.mkv");
result.Should().Be(@"/mnt/something else/Some Shared Folder/Some Movie/Some Movie.mkv");
}
[Test]
public async Task Should_Not_Throw_For_Null_EmbyPath()
{
var replacements = new List<EmbyPathReplacement>
{
new()
{
Id = 1,
EmbyPath = null,
LocalPath = @"/mnt/something else/Some Shared Folder",
EmbyMediaSource = new EmbyMediaSource { OperatingSystem = "Linux" }
}
};
var repo = new Mock<IMediaSourceRepository>();
repo.Setup(x => x.GetEmbyPathReplacementsByLibraryId(It.IsAny<int>())).Returns(replacements.AsTask());
var runtime = new Mock<IRuntimeInfo>();
runtime.Setup(x => x.IsOSPlatform(OSPlatform.Windows)).Returns(false);
var service = new EmbyPathReplacementService(
repo.Object,
runtime.Object,
new Mock<ILogger<EmbyPathReplacementService>>().Object);
string result = await service.GetReplacementEmbyPath(
0,
@"/mnt/something/Some Shared Folder/Some Movie/Some Movie.mkv");
result.Should().Be(@"/mnt/something/Some Shared Folder/Some Movie/Some Movie.mkv");
}
[Test]
public async Task Should_Not_Throw_For_Null_LocalPath()
{
var replacements = new List<EmbyPathReplacement>
{
new()
{
Id = 1,
EmbyPath = @"/mnt/something/Some Shared Folder",
LocalPath = null,
EmbyMediaSource = new EmbyMediaSource { OperatingSystem = "Linux" }
}
};
var repo = new Mock<IMediaSourceRepository>();
repo.Setup(x => x.GetEmbyPathReplacementsByLibraryId(It.IsAny<int>())).Returns(replacements.AsTask());
var runtime = new Mock<IRuntimeInfo>();
runtime.Setup(x => x.IsOSPlatform(OSPlatform.Windows)).Returns(false);
var service = new EmbyPathReplacementService(
repo.Object,
runtime.Object,
new Mock<ILogger<EmbyPathReplacementService>>().Object);
string result = await service.GetReplacementEmbyPath(
0,
@"/mnt/something/Some Shared Folder/Some Movie/Some Movie.mkv");
result.Should().Be(@"/Some Movie/Some Movie.mkv");
}
}

64
ErsatzTV.Core.Tests/Jellyfin/JellyfinPathReplacementServiceTests.cs

@ -204,4 +204,68 @@ public class JellyfinPathReplacementServiceTests
result.Should().Be(@"/mnt/something else/Some Shared Folder/Some Movie/Some Movie.mkv"); result.Should().Be(@"/mnt/something else/Some Shared Folder/Some Movie/Some Movie.mkv");
} }
[Test]
public async Task Should_Not_Throw_For_Null_JellyfinPath()
{
var replacements = new List<JellyfinPathReplacement>
{
new()
{
Id = 1,
JellyfinPath = null,
LocalPath = @"/mnt/something else/Some Shared Folder",
JellyfinMediaSource = new JellyfinMediaSource { OperatingSystem = "Linux" }
}
};
var repo = new Mock<IMediaSourceRepository>();
repo.Setup(x => x.GetJellyfinPathReplacementsByLibraryId(It.IsAny<int>())).Returns(replacements.AsTask());
var runtime = new Mock<IRuntimeInfo>();
runtime.Setup(x => x.IsOSPlatform(OSPlatform.Windows)).Returns(false);
var service = new JellyfinPathReplacementService(
repo.Object,
runtime.Object,
new Mock<ILogger<JellyfinPathReplacementService>>().Object);
string result = await service.GetReplacementJellyfinPath(
0,
@"/mnt/something/Some Shared Folder/Some Movie/Some Movie.mkv");
result.Should().Be(@"/mnt/something/Some Shared Folder/Some Movie/Some Movie.mkv");
}
[Test]
public async Task Should_Not_Throw_For_Null_LocalPath()
{
var replacements = new List<JellyfinPathReplacement>
{
new()
{
Id = 1,
JellyfinPath = @"/mnt/something/Some Shared Folder",
LocalPath = null,
JellyfinMediaSource = new JellyfinMediaSource { OperatingSystem = "Linux" }
}
};
var repo = new Mock<IMediaSourceRepository>();
repo.Setup(x => x.GetJellyfinPathReplacementsByLibraryId(It.IsAny<int>())).Returns(replacements.AsTask());
var runtime = new Mock<IRuntimeInfo>();
runtime.Setup(x => x.IsOSPlatform(OSPlatform.Windows)).Returns(false);
var service = new JellyfinPathReplacementService(
repo.Object,
runtime.Object,
new Mock<ILogger<JellyfinPathReplacementService>>().Object);
string result = await service.GetReplacementJellyfinPath(
0,
@"/mnt/something/Some Shared Folder/Some Movie/Some Movie.mkv");
result.Should().Be(@"/Some Movie/Some Movie.mkv");
}
} }

64
ErsatzTV.Core.Tests/Plex/PlexPathReplacementServiceTests.cs

@ -204,4 +204,68 @@ public class PlexPathReplacementServiceTests
result.Should().Be(@"/mnt/something else/Some Shared Folder/Some Movie/Some Movie.mkv"); result.Should().Be(@"/mnt/something else/Some Shared Folder/Some Movie/Some Movie.mkv");
} }
[Test]
public async Task Should_Not_Throw_For_Null_PlexPath()
{
var replacements = new List<PlexPathReplacement>
{
new()
{
Id = 1,
PlexPath = null,
LocalPath = @"/mnt/something else/Some Shared Folder",
PlexMediaSource = new PlexMediaSource { Platform = "Linux" }
}
};
var repo = new Mock<IMediaSourceRepository>();
repo.Setup(x => x.GetPlexPathReplacementsByLibraryId(It.IsAny<int>())).Returns(replacements.AsTask());
var runtime = new Mock<IRuntimeInfo>();
runtime.Setup(x => x.IsOSPlatform(OSPlatform.Windows)).Returns(false);
var service = new PlexPathReplacementService(
repo.Object,
runtime.Object,
new Mock<ILogger<PlexPathReplacementService>>().Object);
string result = await service.GetReplacementPlexPath(
0,
@"/mnt/something/Some Shared Folder/Some Movie/Some Movie.mkv");
result.Should().Be(@"/mnt/something/Some Shared Folder/Some Movie/Some Movie.mkv");
}
[Test]
public async Task Should_Not_Throw_For_Null_LocalPath()
{
var replacements = new List<PlexPathReplacement>
{
new()
{
Id = 1,
PlexPath = @"/mnt/something/Some Shared Folder",
LocalPath = null,
PlexMediaSource = new PlexMediaSource { Platform = "Linux" }
}
};
var repo = new Mock<IMediaSourceRepository>();
repo.Setup(x => x.GetPlexPathReplacementsByLibraryId(It.IsAny<int>())).Returns(replacements.AsTask());
var runtime = new Mock<IRuntimeInfo>();
runtime.Setup(x => x.IsOSPlatform(OSPlatform.Windows)).Returns(false);
var service = new PlexPathReplacementService(
repo.Object,
runtime.Object,
new Mock<ILogger<PlexPathReplacementService>>().Object);
string result = await service.GetReplacementPlexPath(
0,
@"/mnt/something/Some Shared Folder/Some Movie/Some Movie.mkv");
result.Should().Be(@"/Some Movie/Some Movie.mkv");
}
} }

13
ErsatzTV.Core/Emby/EmbyPathReplacementService.cs

@ -40,6 +40,11 @@ public class EmbyPathReplacementService : IEmbyPathReplacementService
.SingleOrDefault( .SingleOrDefault(
r => r =>
{ {
if (string.IsNullOrWhiteSpace(r.EmbyPath))
{
return false;
}
string separatorChar = IsWindows(r.EmbyMediaSource, path) ? @"\" : @"/"; string separatorChar = IsWindows(r.EmbyMediaSource, path) ? @"\" : @"/";
string prefix = r.EmbyPath.EndsWith(separatorChar) string prefix = r.EmbyPath.EndsWith(separatorChar)
? r.EmbyPath ? r.EmbyPath
@ -47,8 +52,7 @@ public class EmbyPathReplacementService : IEmbyPathReplacementService
return path.StartsWith(prefix); return path.StartsWith(prefix);
}); });
return maybeReplacement.Match( foreach (EmbyPathReplacement replacement in maybeReplacement)
replacement =>
{ {
string finalPath = path.Replace(replacement.EmbyPath, replacement.LocalPath); string finalPath = path.Replace(replacement.EmbyPath, replacement.LocalPath);
if (IsWindows(replacement.EmbyMediaSource, path) && !_runtimeInfo.IsOSPlatform(OSPlatform.Windows)) if (IsWindows(replacement.EmbyMediaSource, path) && !_runtimeInfo.IsOSPlatform(OSPlatform.Windows))
@ -71,8 +75,9 @@ public class EmbyPathReplacementService : IEmbyPathReplacementService
} }
return finalPath; return finalPath;
}, }
() => path);
return path;
} }
private static bool IsWindows(EmbyMediaSource embyMediaSource, string path) private static bool IsWindows(EmbyMediaSource embyMediaSource, string path)

13
ErsatzTV.Core/Jellyfin/JellyfinPathReplacementService.cs

@ -40,6 +40,11 @@ public class JellyfinPathReplacementService : IJellyfinPathReplacementService
.SingleOrDefault( .SingleOrDefault(
r => r =>
{ {
if (string.IsNullOrWhiteSpace(r.JellyfinPath))
{
return false;
}
string separatorChar = IsWindows(r.JellyfinMediaSource, path) ? @"\" : @"/"; string separatorChar = IsWindows(r.JellyfinMediaSource, path) ? @"\" : @"/";
string prefix = r.JellyfinPath.EndsWith(separatorChar) string prefix = r.JellyfinPath.EndsWith(separatorChar)
? r.JellyfinPath ? r.JellyfinPath
@ -47,8 +52,7 @@ public class JellyfinPathReplacementService : IJellyfinPathReplacementService
return path.StartsWith(prefix); return path.StartsWith(prefix);
}); });
return maybeReplacement.Match( foreach (JellyfinPathReplacement replacement in maybeReplacement)
replacement =>
{ {
string finalPath = path.Replace(replacement.JellyfinPath, replacement.LocalPath); string finalPath = path.Replace(replacement.JellyfinPath, replacement.LocalPath);
if (IsWindows(replacement.JellyfinMediaSource, path) && if (IsWindows(replacement.JellyfinMediaSource, path) &&
@ -72,8 +76,9 @@ public class JellyfinPathReplacementService : IJellyfinPathReplacementService
} }
return finalPath; return finalPath;
}, }
() => path);
return path;
} }
private static bool IsWindows(JellyfinMediaSource jellyfinMediaSource, string path) private static bool IsWindows(JellyfinMediaSource jellyfinMediaSource, string path)

13
ErsatzTV.Core/Plex/PlexPathReplacementService.cs

@ -37,13 +37,17 @@ public class PlexPathReplacementService : IPlexPathReplacementService
.SingleOrDefault( .SingleOrDefault(
r => r =>
{ {
if (string.IsNullOrWhiteSpace(r.PlexPath))
{
return false;
}
string separatorChar = IsWindows(r.PlexMediaSource) ? @"\" : @"/"; string separatorChar = IsWindows(r.PlexMediaSource) ? @"\" : @"/";
string prefix = r.PlexPath.EndsWith(separatorChar) ? r.PlexPath : r.PlexPath + separatorChar; string prefix = r.PlexPath.EndsWith(separatorChar) ? r.PlexPath : r.PlexPath + separatorChar;
return path.StartsWith(prefix); return path.StartsWith(prefix);
}); });
return maybeReplacement.Match( foreach (PlexPathReplacement replacement in maybeReplacement)
replacement =>
{ {
string finalPath = path.Replace(replacement.PlexPath, replacement.LocalPath); string finalPath = path.Replace(replacement.PlexPath, replacement.LocalPath);
if (IsWindows(replacement.PlexMediaSource) && !_runtimeInfo.IsOSPlatform(OSPlatform.Windows)) if (IsWindows(replacement.PlexMediaSource) && !_runtimeInfo.IsOSPlatform(OSPlatform.Windows))
@ -65,8 +69,9 @@ public class PlexPathReplacementService : IPlexPathReplacementService
} }
return finalPath; return finalPath;
}, }
() => path);
return path;
} }
private static bool IsWindows(PlexMediaSource plexMediaSource) => private static bool IsWindows(PlexMediaSource plexMediaSource) =>

Loading…
Cancel
Save