Skip to content

Commit 87492bc

Browse files
committed
Remove FFMpeg/FFprobe dependency — unused for game PVR
Games aren't video files. The ffprobe-based media info system was legacy from Radarr that ran ffprobe on every game file import to extract audio codecs, HDR type, subtitles, resolution — all meaningless for ISOs, archives, and installers. Removed Servarr.FFMpegCore and Servarr.FFprobe packages. Stubbed out VideoFileInfoReader, UpdateMediaInfoService, MediaInfoFormatter, and DetectSample to no-ops. Preserved MediaInfoModel and API shapes for DB/API compat. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9334a56 commit 87492bc

File tree

16 files changed

+78
-1444
lines changed

16 files changed

+78
-1444
lines changed
Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
using System;
2-
using NzbDrone.Common.Extensions;
31
using NzbDrone.Core.MediaFiles.MediaInfo;
42
using Gamarr.Http.REST;
53

@@ -33,36 +31,25 @@ public static MediaInfoResource ToResource(this MediaInfoModel model, string sce
3331
return null;
3432
}
3533

34+
// Return empty/default values - media info scanning is not applicable for game files
3635
return new MediaInfoResource
3736
{
38-
AudioBitrate = model.AudioBitrate,
39-
AudioChannels = MediaInfoFormatter.FormatAudioChannels(model),
40-
AudioLanguages = model.AudioLanguages.ConcatToString("/"),
41-
AudioStreamCount = model.AudioStreamCount,
42-
AudioCodec = MediaInfoFormatter.FormatAudioCodec(model, sceneName),
43-
VideoBitDepth = model.VideoBitDepth,
44-
VideoBitrate = model.VideoBitrate,
45-
VideoCodec = MediaInfoFormatter.FormatVideoCodec(model, sceneName),
46-
VideoFps = Math.Round(model.VideoFps, 3),
47-
VideoDynamicRange = MediaInfoFormatter.FormatVideoDynamicRange(model),
48-
VideoDynamicRangeType = MediaInfoFormatter.FormatVideoDynamicRangeType(model),
49-
Resolution = $"{model.Width}x{model.Height}",
50-
RunTime = FormatRuntime(model.RunTime),
51-
ScanType = model.ScanType,
52-
Subtitles = model.Subtitles.ConcatToString("/")
37+
AudioBitrate = 0,
38+
AudioChannels = 0,
39+
AudioLanguages = string.Empty,
40+
AudioStreamCount = 0,
41+
AudioCodec = string.Empty,
42+
VideoBitDepth = 0,
43+
VideoBitrate = 0,
44+
VideoCodec = string.Empty,
45+
VideoFps = 0,
46+
VideoDynamicRange = string.Empty,
47+
VideoDynamicRangeType = string.Empty,
48+
Resolution = string.Empty,
49+
RunTime = string.Empty,
50+
ScanType = string.Empty,
51+
Subtitles = string.Empty
5352
};
5453
}
55-
56-
private static string FormatRuntime(TimeSpan runTime)
57-
{
58-
var hours = (int)runTime.TotalHours;
59-
60-
if (hours > 0)
61-
{
62-
return $"{hours}:{runTime.Minutes:00}:{runTime.Seconds:00}";
63-
}
64-
65-
return $"{runTime.Minutes}:{runTime.Seconds:00}";
66-
}
6754
}
6855
}
Lines changed: 7 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
using System;
21
using FizzWare.NBuilder;
32
using FluentAssertions;
4-
using Moq;
53
using NUnit.Framework;
6-
using NzbDrone.Core.MediaFiles.MediaInfo;
74
using NzbDrone.Core.MediaFiles.GameImport;
85
using NzbDrone.Core.Games;
96
using NzbDrone.Core.Parser.Model;
107
using NzbDrone.Core.Qualities;
118
using NzbDrone.Core.Test.Framework;
12-
using NzbDrone.Test.Common;
139

1410
namespace NzbDrone.Core.Test.MediaFiles.GameImport
1511
{
@@ -34,140 +30,20 @@ public void Setup()
3430
};
3531
}
3632

37-
private void GivenRuntime(int seconds)
38-
{
39-
Mocker.GetMock<IVideoFileInfoReader>()
40-
.Setup(s => s.GetRunTime(It.IsAny<string>()))
41-
.Returns(new TimeSpan(0, 0, seconds));
42-
}
43-
44-
[Test]
45-
public void should_return_false_for_flv()
46-
{
47-
_localGame.Path = @"C:\Test\some.show.s01e01.flv";
48-
49-
ShouldBeNotSample();
50-
51-
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
52-
}
53-
5433
[Test]
55-
public void should_return_false_for_strm()
34+
public void should_always_return_not_sample_for_game_files()
5635
{
57-
_localGame.Path = @"C:\Test\some.show.s01e01.strm";
58-
59-
ShouldBeNotSample();
60-
61-
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
62-
}
63-
64-
[Test]
65-
public void should_return_false_for_iso()
66-
{
67-
_localGame.Path = @"C:\Test\some game (2000).iso";
68-
69-
ShouldBeNotSample();
70-
71-
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
72-
}
73-
74-
[Test]
75-
public void should_return_false_for_img()
76-
{
77-
_localGame.Path = @"C:\Test\some game (2000).img";
78-
79-
ShouldBeNotSample();
80-
81-
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
82-
}
83-
84-
[Test]
85-
public void should_return_false_for_m2ts()
86-
{
87-
_localGame.Path = @"C:\Test\some game (2000).m2ts";
88-
89-
ShouldBeNotSample();
90-
91-
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
92-
}
93-
94-
[Test]
95-
public void should_use_runtime()
96-
{
97-
GivenRuntime(120);
98-
36+
// All game files should return NotSample since ffprobe sample detection is removed
9937
Subject.IsSample(_localGame.Game.GameMetadata,
100-
_localGame.Path);
101-
102-
Mocker.GetMock<IVideoFileInfoReader>().Verify(v => v.GetRunTime(It.IsAny<string>()), Times.Once());
103-
}
104-
105-
[Test]
106-
public void should_return_true_if_runtime_is_less_than_minimum()
107-
{
108-
GivenRuntime(60);
109-
110-
ShouldBeSample();
111-
}
112-
113-
[Test]
114-
public void should_return_false_if_runtime_greater_than_minimum()
115-
{
116-
GivenRuntime(600);
117-
118-
ShouldBeNotSample();
119-
}
120-
121-
[Test]
122-
public void should_return_false_if_runtime_greater_than_webisode_minimum()
123-
{
124-
_game.Runtime = 6;
125-
GivenRuntime(299);
126-
127-
ShouldBeNotSample();
128-
}
129-
130-
[Test]
131-
public void should_return_false_if_runtime_greater_than_anime_short_minimum()
132-
{
133-
_game.Runtime = 2;
134-
GivenRuntime(60);
135-
136-
ShouldBeNotSample();
137-
}
138-
139-
[Test]
140-
public void should_return_true_if_runtime_less_than_anime_short_minimum()
141-
{
142-
_game.Runtime = 2;
143-
GivenRuntime(10);
144-
145-
ShouldBeSample();
38+
_localGame.Path).Should().Be(DetectSampleResult.NotSample);
14639
}
14740

14841
[Test]
149-
public void should_return_indeterminate_if_mediainfo_result_is_null()
150-
{
151-
Mocker.GetMock<IVideoFileInfoReader>()
152-
.Setup(s => s.GetRunTime(It.IsAny<string>()))
153-
.Returns((TimeSpan?)null);
154-
155-
Subject.IsSample(_localGame.Game.GameMetadata,
156-
_localGame.Path).Should().Be(DetectSampleResult.Indeterminate);
157-
158-
ExceptionVerification.ExpectedErrors(1);
159-
}
160-
161-
private void ShouldBeSample()
42+
public void should_return_not_sample_for_any_path()
16243
{
163-
Subject.IsSample(_localGame.Game.GameMetadata,
164-
_localGame.Path).Should().Be(DetectSampleResult.Sample);
165-
}
166-
167-
private void ShouldBeNotSample()
168-
{
169-
Subject.IsSample(_localGame.Game.GameMetadata,
170-
_localGame.Path).Should().Be(DetectSampleResult.NotSample);
44+
Subject.IsSample(_game, @"C:\Test\some.game.exe").Should().Be(DetectSampleResult.NotSample);
45+
Subject.IsSample(_game, @"C:\Test\some.game.iso").Should().Be(DetectSampleResult.NotSample);
46+
Subject.IsSample(_game, @"C:\Test\some.game.doi").Should().Be(DetectSampleResult.NotSample);
17147
}
17248
}
17349
}

src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoFormatterTests/FormatAudioCodecFixture.cs

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,56 +8,17 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
88
[TestFixture]
99
public class FormatAudioCodecFixture : TestBase
1010
{
11-
private static string sceneName = "My.Game.v1.0-Gamarr";
12-
13-
[TestCase("mp2, , ", "droned.s01e03.swedish.720p.hdtv.x264-prince", "MP2")]
14-
[TestCase("vorbis, , ", "DB Super HDTV", "Vorbis")]
15-
[TestCase("pcm_s16le, , ", "DW DVDRip XviD-idTV, ", "PCM")]
16-
[TestCase("truehd, , ", "", "TrueHD")]
17-
[TestCase("truehd, , ", "TrueHD", "TrueHD")]
18-
[TestCase("truehd, thd+, ", "Atmos", "TrueHD Atmos")]
19-
[TestCase("truehd, thd+, ", "TrueHD.Atmos.7.1", "TrueHD Atmos")]
20-
[TestCase("truehd, thd+, ", "", "TrueHD Atmos")]
21-
[TestCase("wmav1, , ", "Cyberpunk.2077.wmv", "WMA")]
22-
[TestCase("wmav2, , ", "B.N.S04E18.720p.WEB-DL", "WMA")]
23-
[TestCase("opus, , ", "Roadkill Ep3x11 - YouTube.webm", "Opus")]
24-
[TestCase("mp3, , ", "climbing.mp4", "MP3")]
25-
[TestCase("dts, , DTS-HD MA", "DTS-HD.MA", "DTS-HD MA")]
26-
[TestCase("dts, , DTS:X", "DTS-X", "DTS-X")]
27-
[TestCase("dts, , DTS-HD MA", "DTS-HD.MA", "DTS-HD MA")]
28-
[TestCase("dts, , DTS-ES", "DTS-ES", "DTS-ES")]
29-
[TestCase("dts, , DTS-ES", "DTS", "DTS-ES")]
30-
[TestCase("dts, , DTS-HD HRA", "DTSHD-HRA", "DTS-HD HRA")]
31-
[TestCase("dts, , DTS", "DTS", "DTS")]
32-
[TestCase("eac3, ec+3, ", "EAC3.Atmos", "EAC3 Atmos")]
33-
[TestCase("eac3, , ", "DDP5.1", "EAC3")]
34-
[TestCase("ac3, , ", "DD5.1", "AC3")]
35-
[TestCase("adpcm_ima_qt, , ", "Custom?", "PCM")]
36-
[TestCase("adpcm_ms, , ", "Custom", "PCM")]
37-
38-
public void should_format_audio_format(string audioFormatPack, string sceneName, string expectedFormat)
39-
{
40-
var split = audioFormatPack.Split(new string[] { ", " }, System.StringSplitOptions.None);
41-
var mediaInfoModel = new MediaInfoModel
42-
{
43-
AudioFormat = split[0],
44-
AudioCodecID = split[1],
45-
AudioProfile = split[2]
46-
};
47-
48-
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
49-
}
50-
5111
[Test]
52-
public void should_return_AudioFormat_by_default()
12+
public void should_return_empty_string()
5313
{
5414
var mediaInfoModel = new MediaInfoModel
5515
{
56-
AudioFormat = "Other Audio Format",
57-
AudioCodecID = "Other Audio Codec"
16+
AudioFormat = "aac",
17+
AudioCodecID = "mp4a",
18+
AudioProfile = "LC"
5819
};
5920

60-
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel, sceneName).Should().Be(mediaInfoModel.AudioFormat);
21+
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel, "scene").Should().Be(string.Empty);
6122
}
6223
}
6324
}

src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoFormatterTests/FormatVideoCodecFixture.cs

Lines changed: 4 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -8,75 +8,16 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
88
[TestFixture]
99
public class FormatVideoCodecFixture : TestBase
1010
{
11-
[TestCase("mpeg2video, ", "Cyberpunk.2077.1080p.MPEG2-RELOADED", "MPEG2")]
12-
[TestCase("mpeg2video, ", "", "MPEG2")]
13-
[TestCase("mpeg1video, ", "The.Series.S13E04.INTERNAL-ANiVCD.mpg", "MPEG")]
14-
[TestCase("vc1, WVC1", "T.N.S04E18.720p.WEB-DL", "VC1")]
15-
[TestCase("vc1, V_MS/VFW/FOURCC/WVC1", "", "VC1")]
16-
[TestCase("vc1, WMV3", "Series Title S09E13 The Gamarr's RevengeHDTV.XviD-2HD.avi", "VC1")]
17-
[TestCase("h264, V.MPEG4/ISO/AVC", "Series.2015.S03E08.720p.iP.WEBRip.AAC2.0.H264-BTW", "h264")]
18-
[TestCase("h264, V_MPEG4/ISO/AVC", "Serie.2019.S01E03.1080p.RTE.WEB-DL.AAC2.0.x264-RTN", "x264")]
19-
[TestCase("wmv1, WMV1", "Cyberpunk.2077.wmv", "WMV")]
20-
[TestCase("wmv2, WMV2", "Cyberpunk.2077.wmv", "WMV")]
21-
[TestCase("mpeg4, XVID", "", "XviD")]
22-
[TestCase("mpeg4, DIVX", "", "DivX")]
23-
[TestCase("mpeg4, divx", "", "DivX")]
24-
[TestCase("mpeg4, DIV3", "spsm.dvdrip.divx.avi'.", "DivX")]
25-
[TestCase("msmpeg4, DIV3", "Game the Title (1976) 360p MPEG Audio.avi", "DivX")]
26-
[TestCase("msmpeg4v2, DIV3", "Game the Title (1976) 360p MPEG Audio.avi", "DivX")]
27-
[TestCase("msmpeg4v3, DIV3", "Game the Title (1976) 360p MPEG Audio.avi", "DivX")]
28-
[TestCase("vp6f, 4", "Game the Title (1976) 360p MPEG Audio.flv", "VP6")]
29-
[TestCase("vp6, 4", "Top Gear - S12E01 - Lorries - SD TV.flv", "VP6")]
30-
[TestCase("vp7, VP70", "Game the Title.avi", "VP7")]
31-
[TestCase("vp8, V_VP8", "Game the Title.mkv", "VP8")]
32-
[TestCase("vp9, V_VP9", "Series the Title Ep3x11 - YouTube.webm", "VP9")]
33-
[TestCase("h264, x264", "Series the Title - S04E05 - Stanley Hotel SDTV.avi", "x264")]
34-
[TestCase("hevc, V_MPEGH/ISO/HEVC", "Series the Title S11E12 The Gamarr Metric 1080p 10bit AMZN WEB-DL", "h265")]
35-
[TestCase("mpeg4, mp4v-20", "", "")]
36-
[TestCase("mpeg4, XVID", "Series the Title.S06E07.Mountain.Gamarr.DSR.XviD-KRS", "XviD")]
37-
[TestCase("rpza, V_QUICKTIME", "Custom", "")]
38-
[TestCase("mpeg4, FMP4", "", "")]
39-
[TestCase("mpeg4, MP42", "", "")]
40-
[TestCase("mpeg4, mp43", "Series the Title.S01E13.480p.WEB-DL.H.264-BTN-Custom", "")]
41-
public void should_format_video_format(string videoFormatPack, string sceneName, string expectedFormat)
42-
{
43-
var split = videoFormatPack.Split(new string[] { ", " }, System.StringSplitOptions.None);
44-
var mediaInfoModel = new MediaInfoModel
45-
{
46-
VideoFormat = split[0],
47-
VideoCodecID = split[1]
48-
};
49-
50-
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
51-
}
52-
53-
[TestCase("h264, x264", "Some.Video.S01E01.h264", "x264")] // Force mediainfo tag
54-
[TestCase("hevc, x265", "Some.Video.S01E01.h265", "x265")] // Force mediainfo tag
55-
[TestCase("h264, ", "Some.Video.S01E01.x264", "x264")] // Not seen in practice, but honor tag if otherwise unknown
56-
[TestCase("hevc, ", "Some.Video.S01E01.x265", "x265")] // Not seen in practice, but honor tag if otherwise unknown
57-
[TestCase("h264, ", "Some.Video.S01E01", "h264")] // Default value
58-
[TestCase("hevc, ", "Some.Video.S01E01", "h265")] // Default value
59-
public void should_format_video_format_fallbacks(string videoFormatPack, string sceneName, string expectedFormat)
60-
{
61-
var split = videoFormatPack.Split(new string[] { ", " }, System.StringSplitOptions.None);
62-
var mediaInfoModel = new MediaInfoModel
63-
{
64-
VideoFormat = split[0],
65-
VideoCodecID = split[1]
66-
};
67-
68-
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
69-
}
70-
7111
[Test]
72-
public void should_return_VideoFormat_by_default()
12+
public void should_return_empty_string()
7313
{
7414
var mediaInfoModel = new MediaInfoModel
7515
{
76-
VideoFormat = "VideoCodec"
16+
VideoFormat = "h264",
17+
VideoCodecID = "avc1"
7718
};
7819

79-
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, null).Should().Be(mediaInfoModel.VideoFormat);
20+
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, "scene").Should().Be(string.Empty);
8021
}
8122
}
8223
}

src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoFormatterTests/FormatVideoDynamicRangeFixture.cs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,16 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
88
[TestFixture]
99
public class FormatVideoDynamicRangeFixture : TestBase
1010
{
11-
[TestCase(HdrFormat.None, "")]
12-
[TestCase(HdrFormat.Hlg10, "HDR")]
13-
[TestCase(HdrFormat.Pq10, "HDR")]
14-
[TestCase(HdrFormat.Hdr10, "HDR")]
15-
[TestCase(HdrFormat.Hdr10Plus, "HDR")]
16-
[TestCase(HdrFormat.DolbyVision, "HDR")]
17-
public void should_format_video_dynamic_range(HdrFormat format, string expectedVideoDynamicRange)
11+
[Test]
12+
public void should_return_empty_string()
1813
{
1914
var mediaInfo = new MediaInfoModel
2015
{
21-
VideoHdrFormat = format,
16+
VideoHdrFormat = HdrFormat.Hdr10,
2217
SchemaRevision = 8
2318
};
2419

25-
MediaInfoFormatter.FormatVideoDynamicRange(mediaInfo).Should().Be(expectedVideoDynamicRange);
20+
MediaInfoFormatter.FormatVideoDynamicRange(mediaInfo).Should().Be(string.Empty);
2621
}
2722
}
2823
}

0 commit comments

Comments
 (0)