Refactoring media browser - entity resolution
over 15 years ago
Media browser has a bunch Entities that it deals with, for example: Movies, Episodes and Shows. We scan the file system and figure out what files and folders map to what entities.
This is done using entity resolution.
I have defined a new set of classes that take in a location and spit out a factory that knows how to create an entity. Sounds a bit tricky, but this trickiness gives us tons of flexibility. The beauty of this system is that code now lives in a very logical place. If a user is trying to figure out why a particular file is not being detected as a Movie we know to look at the MovieResolver which contains all the logic for movie resolution. Additionally, this architecture is very plug-in friendly and incredibly testable.
For example, I just fixed up a bug where folders were not being detected as movies properly.
Writing the fix was really easy, first I started with this test case:
[Test]
public void TestRecusiveMovieResolution()
{
var resolver = resolver = new ChainedEntityResolver() {
new MovieResolver(2, true), // maximum 2 videos per movie, allow for recursive search
new FolderResolver()
};
var movieFolder = MockFolderMediaLocation.CreateMockLocation(@"
|Rushmore
|part 1
a.avi
|part 2
b.avi
");
Assert.AreEqual(resolver.ResolveType(movieFolder), typeof(Movie));
}
Some observations:
- I wrote an awesome little mock filesystem that allows us to test our algorithms without creating files.
- We do chained resolution, meaning that if a resolver at the top of the chain resolves the type of a location we stop and return.
Next comes the fix in MovieResolver.cs:
videoCount += childFolders
.Select(child => ChildVideos(child))
.SelectMany(x => x)
.Take((maxVideosPerMovie - videoCount) + 1)
.Count();
</code></pre>
and
private IEnumerable<IMediaLocation> ChildVideos(IFolderMediaLocation location) {
foreach (var child in location.Children) {
if (child.IsVideo()) {
yield return child;
}
var folder = child as IFolderMediaLocation;
if (folder != null) {
foreach (var grandChild in ChildVideos(folder)) {
yield return grandChild;
}
}
}
}
Observations:
- SelectMany is a really useful LINQ method, that allows you to lazily chain your enumerables.
Thanks Sam!
I'd like to contribute once I get my development environment up and running, problem is I'm running Enterprise and that does not include the MC dlls.
Stephen