Add multi-performer support to the cue sheet parser.

Fix an incorrect early-out in the cue sheet line parsing function.
Fix a crash caused by the invalid artist and album on the local tracks.
Add support to remove duplicated cue sheet files.

(cherry picked from commit 336c62e6141d67c1f547806f09d4871d26342dca)
pull/4200/head
zhangdoa 6 months ago
parent 0bb5a10f10
commit 88abe75927

@ -15,7 +15,7 @@ namespace NzbDrone.Core.MediaFiles
{
public int Number { get; set; }
public string Title { get; set; }
public string Performer { get; set; }
public List<string> Performers { get; set; } = new List<string>();
public List<IndexEntry> Indices { get; set; } = new List<IndexEntry>();
}
@ -33,6 +33,6 @@ namespace NzbDrone.Core.MediaFiles
public string Date { get; set; }
public string DiscID { get; set; }
public string Title { get; set; }
public string Performer { get; set; }
public List<string> Performers { get; set; } = new List<string>();
}
}

@ -5,7 +5,6 @@ using System.IO.Abstractions;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.MediaFiles.TrackImport;
using NzbDrone.Core.Music;
using NzbDrone.Core.Parser;
@ -61,7 +60,25 @@ namespace NzbDrone.Core.MediaFiles
foreach (var cueFile in cueFiles)
{
var cueSheetInfo = GetCueSheetInfo(cueFile, mediaFileList);
cueSheetInfos.Add(cueSheetInfo);
var addedCueSheetInfo = cueSheetInfos.Find(existingCueSheetInfo => existingCueSheetInfo.CueSheet.DiscID == cueSheetInfo.CueSheet.DiscID);
if (addedCueSheetInfo == null)
{
cueSheetInfos.Add(cueSheetInfo);
}
// If there are multiple cue sheet files for the same disc, then we try to keep the last one or the one with the exact same name as the media file, if there's any
else if (cueSheetInfo.CueSheet.IsSingleFileRelease && addedCueSheetInfo.CueSheet.Files.Count > 0)
{
var mediaFileName = Path.GetFileName(addedCueSheetInfo.CueSheet.Files[0].Name);
var cueSheetFileName = Path.GetFileName(cueFile.Name);
if (mediaFileName != cueSheetFileName)
{
cueSheetInfos.Remove(addedCueSheetInfo);
cueSheetInfos.Add(cueSheetInfo);
}
}
}
var cueSheetInfosGroupedByDiscId = cueSheetInfos.GroupBy(x => x.CueSheet.DiscID).ToList();
@ -120,6 +137,11 @@ namespace NzbDrone.Core.MediaFiles
return;
}
if (decision.Item.Release == null)
{
return;
}
var tracksFromRelease = decision.Item.Release.Tracks.Value;
if (tracksFromRelease.Count == 0)
{
@ -172,124 +194,142 @@ namespace NzbDrone.Core.MediaFiles
return "";
}
private List<string> ExtractPerformers(string line)
{
var delimiters = new char[] { ',', ';' };
var performers = ExtractValue(line, _PerformerKey);
return performers.Split(delimiters, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToList();
}
private bool GetNewLine(ref int index, ref string newLine, string[] lines)
{
if (index < lines.Length)
{
newLine = lines[index];
index++;
return true;
}
return false;
}
private CueSheet ParseLines(string[] lines)
{
var cueSheet = new CueSheet();
var i = 0;
try
string line = null;
while (GetNewLine(ref i, ref line, lines))
{
while (true)
if (line.StartsWith(_FileKey))
{
var line = lines[i];
if (line.StartsWith(_FileKey))
line = line.Trim();
line = line.Substring(_FileKey.Length).Trim();
var filename = line.Split('"')[1];
var fileDetails = new CueSheet.FileEntry { Name = filename };
if (!GetNewLine(ref i, ref line, lines))
{
break;
}
while (line.StartsWith(" "))
{
line = line.Trim();
line = line.Substring(_FileKey.Length).Trim();
var filename = line.Split('"')[1];
var fileDetails = new CueSheet.FileEntry { Name = filename };
if (line.StartsWith(_TrackKey))
{
line = line.Substring(_TrackKey.Length).Trim();
}
i++;
line = lines[i];
while (line.StartsWith(" "))
var trackDetails = new CueSheet.TrackEntry();
var trackInfo = line.Split(' ');
if (trackInfo.Length > 0)
{
line = line.Trim();
if (line.StartsWith(_TrackKey))
if (int.TryParse(trackInfo[0], out var number))
{
line = line.Substring(_TrackKey.Length).Trim();
trackDetails.Number = number;
}
}
var trackDetails = new CueSheet.TrackEntry();
var trackInfo = line.Split(' ');
if (trackInfo.Length > 0)
{
if (int.TryParse(trackInfo[0], out var number))
{
trackDetails.Number = number;
}
}
if (!GetNewLine(ref i, ref line, lines))
{
break;
}
i++;
line = lines[i];
while (line.StartsWith(" "))
while (line.StartsWith(" "))
{
line = line.Trim();
if (line.StartsWith(_IndexKey))
{
line = line.Trim();
if (line.StartsWith(_IndexKey))
line = line.Substring(_IndexKey.Length).Trim();
var parts = line.Split(' ');
if (parts.Length > 1)
{
line = line.Substring(_IndexKey.Length).Trim();
var parts = line.Split(' ');
if (parts.Length > 1)
if (int.TryParse(parts[0], out var key))
{
if (int.TryParse(parts[0], out var key))
{
var value = parts[1].Trim('"');
trackDetails.Indices.Add(new CueSheet.IndexEntry { Key = key, Time = value });
}
var value = parts[1].Trim('"');
trackDetails.Indices.Add(new CueSheet.IndexEntry { Key = key, Time = value });
}
i++;
line = lines[i];
}
else if (line.StartsWith(_TitleKey))
{
trackDetails.Title = ExtractValue(line, _TitleKey);
i++;
line = lines[i];
}
else if (line.StartsWith(_PerformerKey))
{
trackDetails.Performer = ExtractValue(line, _PerformerKey);
i++;
line = lines[i];
}
else
{
i++;
line = lines[i];
}
}
else if (line.StartsWith(_TitleKey))
{
trackDetails.Title = ExtractValue(line, _TitleKey);
}
else if (line.StartsWith(_PerformerKey))
{
trackDetails.Performers = ExtractPerformers(line);
}
fileDetails.Tracks.Add(trackDetails);
if (!GetNewLine(ref i, ref line, lines))
{
break;
}
}
cueSheet.Files.Add(fileDetails);
}
else if (line.StartsWith(_GenreKey))
{
cueSheet.Genre = ExtractValue(line, _GenreKey);
i++;
}
else if (line.StartsWith(_DateKey))
{
cueSheet.Date = ExtractValue(line, _DateKey);
i++;
}
else if (line.StartsWith(_DiscIdKey))
{
cueSheet.DiscID = ExtractValue(line, _DiscIdKey);
i++;
}
else if (line.StartsWith(_PerformerKey))
{
cueSheet.Performer = ExtractValue(line, _PerformerKey);
i++;
}
else if (line.StartsWith(_TitleKey))
{
cueSheet.Title = ExtractValue(line, _TitleKey);
i++;
}
else
{
i++;
fileDetails.Tracks.Add(trackDetails);
}
cueSheet.Files.Add(fileDetails);
}
else if (line.StartsWith(_GenreKey))
{
cueSheet.Genre = ExtractValue(line, _GenreKey);
}
else if (line.StartsWith(_DateKey))
{
cueSheet.Date = ExtractValue(line, _DateKey);
}
else if (line.StartsWith(_DiscIdKey))
{
cueSheet.DiscID = ExtractValue(line, _DiscIdKey);
}
else if (line.StartsWith(_PerformerKey))
{
cueSheet.Performers = ExtractPerformers(line);
}
else if (line.StartsWith(_TitleKey))
{
cueSheet.Title = ExtractValue(line, _TitleKey);
}
}
catch (IndexOutOfRangeException)
return cueSheet;
}
private Artist GetArtist(List<string> performers)
{
if (performers.Count == 1)
{
return _parsingService.GetArtist(performers[0]);
}
else if (performers.Count > 1)
{
return _parsingService.GetArtist("Various Artist");
}
return cueSheet;
return null;
}
private CueSheetInfo GetCueSheetInfo(IFileInfo cueFile, List<IFileInfo> musicFiles)
@ -302,22 +342,37 @@ namespace NzbDrone.Core.MediaFiles
}
cueSheetInfo.CueSheet = cueSheet;
cueSheetInfo.MusicFiles = musicFiles.Where(musicFile => cueSheet.Files.Any(musicFileFromCue => musicFileFromCue.Name == musicFile.Name)).ToList();
cueSheetInfo.IdOverrides = new IdentificationOverrides();
Artist artistFromCue = null;
if (!cueSheet.Performer.Empty())
var artistFromCue = GetArtist(cueSheet.Performers);
if (artistFromCue == null && cueSheet.Files.Count > 0)
{
artistFromCue = _parsingService.GetArtist(cueSheet.Performer);
if (artistFromCue != null)
foreach (var fileEntry in cueSheet.Files)
{
cueSheetInfo.IdOverrides.Artist = artistFromCue;
foreach (var track in fileEntry.Tracks)
{
artistFromCue = GetArtist(track.Performers);
if (artistFromCue != null)
{
break;
}
}
}
}
// The cue sheet file is too incomplete in this case
if (artistFromCue == null)
{
return cueSheetInfo;
}
var parsedAlbumInfo = new ParsedAlbumInfo
{
AlbumTitle = cueSheet.Title,
ArtistName = artistFromCue?.Name,
ArtistName = artistFromCue.Name,
ReleaseDate = cueSheet.Date,
};
@ -327,8 +382,6 @@ namespace NzbDrone.Core.MediaFiles
cueSheetInfo.IdOverrides.Album = albumsFromCue[0];
}
cueSheetInfo.MusicFiles = musicFiles.Where(musicFile => cueSheet.Files.Any(musicFileFromCue => musicFileFromCue.Name == musicFile.Name)).ToList();
return cueSheetInfo;
}
}

@ -69,11 +69,11 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Aggregation.Aggregators
{
for (var i = 0; i < tracks.Count; ++i)
{
tracks[i].FileTrackInfo.ArtistTitle = tracks[i].Artist.Name;
tracks[i].FileTrackInfo.AlbumTitle = tracks[i].Album.Title;
tracks[i].FileTrackInfo.ArtistTitle = tracks[i].Artist?.Name;
tracks[i].FileTrackInfo.AlbumTitle = tracks[i].Album?.Title;
// TODO this is too bold, the release year is not the one from the .cue file
tracks[i].FileTrackInfo.Year = (uint)tracks[i].Album.ReleaseDate.Value.Year;
tracks[i].FileTrackInfo.Year = (uint)(tracks[i].Album?.ReleaseDate?.Year ?? 0);
}
}
else

Loading…
Cancel
Save