mirror of https://github.com/lidarr/Lidarr
Add multi-file/track support to the cue sheet loader.
Rename "cuesheet" to "cue sheet". (cherry picked from commit c9686684ea82e7af8ad203d1bcbb7983adb9e293)
This commit is contained in:
parent
c87efacb6a
commit
a7f07df588
|
@ -59,8 +59,8 @@ const columns = [
|
|||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'cuesheetPath',
|
||||
label: () => 'Cuesheet Path',
|
||||
name: 'cueSheetPath',
|
||||
label: () => 'Cue Sheet Path',
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
|
@ -446,7 +446,7 @@ class InteractiveImportModalContent extends Component {
|
|||
onSelectedChange={this.onSelectedChange}
|
||||
onValidRowChange={this.onValidRowChange}
|
||||
isSingleFileRelease={item.isSingleFileRelease}
|
||||
cuesheetPath={item.cuesheetPath}
|
||||
cueSheetPath={item.cueSheetPath}
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
|
|
@ -135,7 +135,7 @@ class InteractiveImportModalContentConnector extends Component {
|
|||
albumReleaseId,
|
||||
tracks,
|
||||
isSingleFileRelease,
|
||||
cuesheetPath,
|
||||
cueSheetPath,
|
||||
quality,
|
||||
disableReleaseSwitching
|
||||
} = item;
|
||||
|
@ -150,7 +150,7 @@ class InteractiveImportModalContentConnector extends Component {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!(isSingleFileRelease && cuesheetPath) && (!tracks || !tracks.length)) {
|
||||
if (!(isSingleFileRelease && cueSheetPath) && (!tracks || !tracks.length)) {
|
||||
this.setState({ interactiveImportErrorMessage: 'One or more tracks must be chosen for each selected file' });
|
||||
return false;
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ class InteractiveImportModalContentConnector extends Component {
|
|||
albumReleaseId,
|
||||
trackIds: _.map(tracks, 'id'),
|
||||
isSingleFileRelease: item.isSingleFileRelease,
|
||||
cuesheetPath: item.cuesheetPath,
|
||||
cueSheetPath: item.cueSheetPath,
|
||||
quality,
|
||||
downloadId: this.props.downloadId,
|
||||
disableReleaseSwitching
|
||||
|
|
|
@ -169,7 +169,7 @@ class InteractiveImportRow extends Component {
|
|||
albumReleaseId,
|
||||
tracks,
|
||||
isSingleFileRelease,
|
||||
cuesheetPath,
|
||||
cueSheetPath,
|
||||
quality,
|
||||
releaseGroup,
|
||||
size,
|
||||
|
@ -284,10 +284,10 @@ class InteractiveImportRow extends Component {
|
|||
|
||||
<TableRowCell
|
||||
id={id}
|
||||
title={'Cuesheet Path'}
|
||||
title={'Cue Sheet Path'}
|
||||
>
|
||||
{
|
||||
cuesheetPath
|
||||
cueSheetPath
|
||||
}
|
||||
</TableRowCell>
|
||||
|
||||
|
@ -432,7 +432,7 @@ InteractiveImportRow.propTypes = {
|
|||
albumReleaseId: PropTypes.number,
|
||||
tracks: PropTypes.arrayOf(PropTypes.object),
|
||||
isSingleFileRelease: PropTypes.bool.isRequired,
|
||||
cuesheetPath: PropTypes.string.isRequired,
|
||||
cueSheetPath: PropTypes.string.isRequired,
|
||||
releaseGroup: PropTypes.string,
|
||||
quality: PropTypes.object,
|
||||
size: PropTypes.number.isRequired,
|
||||
|
|
|
@ -207,7 +207,7 @@ export const actionHandlers = handleThunks({
|
|||
albumReleaseId: item.albumReleaseId ? item.albumReleaseId : undefined,
|
||||
trackIds: (item.tracks || []).map((e) => e.id),
|
||||
isSingleFileRelease: item.isSingleFileRelease,
|
||||
cuesheetPath: item.cuesheetPath,
|
||||
cueSheetPath: item.cueSheetPath,
|
||||
quality: item.quality,
|
||||
releaseGroup: item.releaseGroup,
|
||||
downloadId: item.downloadId,
|
||||
|
|
|
@ -85,7 +85,7 @@ namespace Lidarr.Api.V1.ManualImport
|
|||
ReplaceExistingFiles = resource.ReplaceExistingFiles,
|
||||
DisableReleaseSwitching = resource.DisableReleaseSwitching,
|
||||
IsSingleFileRelease = resource.IsSingleFileRelease,
|
||||
CuesheetPath = resource.CuesheetPath,
|
||||
CueSheetPath = resource.CueSheetPath,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace Lidarr.Api.V1.ManualImport
|
|||
public bool ReplaceExistingFiles { get; set; }
|
||||
public bool DisableReleaseSwitching { get; set; }
|
||||
public bool IsSingleFileRelease { get; set; }
|
||||
public string CuesheetPath { get; set; }
|
||||
public string CueSheetPath { get; set; }
|
||||
}
|
||||
|
||||
public static class ManualImportResourceMapper
|
||||
|
@ -55,7 +55,7 @@ namespace Lidarr.Api.V1.ManualImport
|
|||
Quality = model.Quality,
|
||||
ReleaseGroup = model.ReleaseGroup,
|
||||
IsSingleFileRelease = model.IsSingleFileRelease,
|
||||
CuesheetPath = model.CuesheetPath,
|
||||
CueSheetPath = model.CueSheetPath,
|
||||
|
||||
// QualityWeight
|
||||
DownloadId = model.DownloadId,
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace Lidarr.Api.V1.ManualImport
|
|||
public bool ReplaceExistingFiles { get; set; }
|
||||
public bool DisableReleaseSwitching { get; set; }
|
||||
public bool IsSingleFileRelease { get; set; }
|
||||
public string CuesheetPath { get; set; }
|
||||
public string CueSheetPath { get; set; }
|
||||
public IEnumerable<Rejection> Rejections { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ namespace NzbDrone.Core.MediaFiles
|
|||
{
|
||||
public CueSheet(IFileInfo fileInfo)
|
||||
{
|
||||
Path = fileInfo.FullName;
|
||||
|
||||
using (var fs = fileInfo.OpenRead())
|
||||
{
|
||||
var bytes = new byte[fileInfo.Length];
|
||||
|
@ -23,58 +25,66 @@ namespace NzbDrone.Core.MediaFiles
|
|||
var lines = content.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
|
||||
|
||||
// Single-file cue means it's an unsplit image
|
||||
var fileNames = ReadFieldFromCuesheet(lines, "FILE");
|
||||
IsSingleFileRelease = fileNames.Count == 1;
|
||||
FileName = fileNames[0];
|
||||
FileNames = ReadField(lines, "FILE");
|
||||
IsSingleFileRelease = FileNames.Count == 1;
|
||||
|
||||
var performers = ReadFieldFromCuesheet(lines, "PERFORMER");
|
||||
var performers = ReadField(lines, "PERFORMER");
|
||||
if (performers.Count > 0)
|
||||
{
|
||||
Performer = performers[0];
|
||||
}
|
||||
|
||||
var titles = ReadFieldFromCuesheet(lines, "TITLE");
|
||||
var titles = ReadField(lines, "TITLE");
|
||||
if (titles.Count > 0)
|
||||
{
|
||||
Title = titles[0];
|
||||
}
|
||||
|
||||
Date = ReadOptionalFieldFromCuesheet(lines, "REM DATE");
|
||||
var dates = ReadField(lines, "REM DATE");
|
||||
if (dates.Count > 0)
|
||||
{
|
||||
Date = dates[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
public bool IsSingleFileRelease { get; set; }
|
||||
public string FileName { get; set; }
|
||||
public List<string> FileNames { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Performer { get; set; }
|
||||
public string Date { get; set; }
|
||||
|
||||
private static List<string> ReadFieldFromCuesheet(string[] lines, string fieldName)
|
||||
private static List<string> ReadField(string[] lines, string fieldName)
|
||||
{
|
||||
var inQuotePattern = "\"(.*?)\"";
|
||||
var flatPattern = fieldName + " (.+)";
|
||||
|
||||
var results = new List<string>();
|
||||
var candidates = lines.Where(l => l.StartsWith(fieldName)).ToList();
|
||||
foreach (var candidate in candidates)
|
||||
{
|
||||
var matches = Regex.Matches(candidate, "\"(.*?)\"");
|
||||
var result = matches.ToList()[0].Groups[1].Value;
|
||||
results.Add(result);
|
||||
var matches = Regex.Matches(candidate, inQuotePattern).ToList();
|
||||
if (matches.Count == 0)
|
||||
{
|
||||
matches = Regex.Matches(candidate, flatPattern).ToList();
|
||||
}
|
||||
|
||||
if (matches.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var groups = matches[0].Groups;
|
||||
if (groups.Count > 0)
|
||||
{
|
||||
var result = groups[1].Value;
|
||||
results.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private static string ReadOptionalFieldFromCuesheet(string[] lines, string fieldName)
|
||||
{
|
||||
var results = lines.Where(l => l.StartsWith(fieldName));
|
||||
if (results.Any())
|
||||
{
|
||||
var matches = Regex.Matches(results.ToList()[0], fieldName + " (.+)");
|
||||
var result = matches.ToList()[0].Groups[1].Value;
|
||||
return result;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,18 +91,18 @@ namespace NzbDrone.Core.MediaFiles
|
|||
|
||||
EnsureTrackFolder(trackFile, localTrack, filePath);
|
||||
|
||||
if (!localTrack.CuesheetPath.Empty())
|
||||
if (localTrack.IsSingleFileRelease && !localTrack.CueSheetPath.Empty())
|
||||
{
|
||||
var directory = Path.GetDirectoryName(filePath);
|
||||
var fileName = Path.GetFileNameWithoutExtension(filePath);
|
||||
var cuesheetPath = Path.Combine(directory, fileName + ".cue");
|
||||
_diskTransferService.TransferFile(localTrack.CuesheetPath, cuesheetPath, TransferMode.Copy);
|
||||
var lines = new List<string>(File.ReadAllLines(cuesheetPath));
|
||||
var cueSheetPath = Path.Combine(directory, fileName + ".cue");
|
||||
_diskTransferService.TransferFile(localTrack.CueSheetPath, cueSheetPath, TransferMode.Copy);
|
||||
var lines = new List<string>(File.ReadAllLines(cueSheetPath));
|
||||
var fileLineIndex = lines.FindIndex(line => line.Contains("FILE"));
|
||||
if (fileLineIndex != -1)
|
||||
{
|
||||
lines[fileLineIndex] = "FILE \"" + Path.GetFileName(filePath) + "\" WAVE";
|
||||
File.WriteAllLines(cuesheetPath, lines);
|
||||
File.WriteAllLines(cueSheetPath, lines);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ namespace NzbDrone.Core.MediaFiles.TrackImport
|
|||
public DownloadClientItem DownloadClientItem { get; set; }
|
||||
public ParsedAlbumInfo ParsedAlbumInfo { get; set; }
|
||||
public bool IsSingleFileRelease { get; set; }
|
||||
public CueSheet CueSheet { get; set; }
|
||||
}
|
||||
|
||||
public class ImportDecisionMakerConfig
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
|||
public string DownloadId { get; set; }
|
||||
public bool DisableReleaseSwitching { get; set; }
|
||||
public bool IsSingleFileRelease { get; set; }
|
||||
public string CuesheetPath { get; set; }
|
||||
public string CueSheetPath { get; set; }
|
||||
|
||||
public bool Equals(ManualImportFile other)
|
||||
{
|
||||
|
|
|
@ -33,6 +33,6 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
|||
public bool ReplaceExistingFiles { get; set; }
|
||||
public bool DisableReleaseSwitching { get; set; }
|
||||
public bool IsSingleFileRelease { get; set; }
|
||||
public string CuesheetPath { get; set; }
|
||||
public string CueSheetPath { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -165,7 +165,18 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
|||
artistFromCue = _parsingService.GetArtist(cueSheet.Performer);
|
||||
}
|
||||
|
||||
var audioFile = audioFiles.Find(x => x.Name == cueSheet.FileName && x.DirectoryName == cueFile.DirectoryName);
|
||||
if (artistFromCue == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO use the audio files from the cue sheet
|
||||
var validAudioFiles = audioFiles.FindAll(x => cueSheet.FileNames.Contains(x.Name));
|
||||
if (validAudioFiles.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var parsedAlbumInfo = new ParsedAlbumInfo
|
||||
{
|
||||
AlbumTitle = cueSheet.Title,
|
||||
|
@ -178,18 +189,16 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
|||
continue;
|
||||
}
|
||||
|
||||
var tempAudioFiles = new List<IFileInfo> { audioFile };
|
||||
|
||||
results.AddRange(ProcessFolder(downloadId, artistFromCue, albumsFromCue[0], filter, replaceExistingFiles, downloadClientItem, cueSheet.Title, tempAudioFiles, cueFile.FullName));
|
||||
audioFiles.Remove(audioFile);
|
||||
results.AddRange(ProcessFolder(downloadId, artistFromCue, albumsFromCue[0], filter, replaceExistingFiles, downloadClientItem, cueSheet.Title, validAudioFiles, cueSheet));
|
||||
audioFiles.RemoveAll(x => validAudioFiles.Contains(x));
|
||||
}
|
||||
|
||||
results.AddRange(ProcessFolder(downloadId, artist, null, filter, replaceExistingFiles, downloadClientItem, directoryInfo.Name, audioFiles, string.Empty));
|
||||
results.AddRange(ProcessFolder(downloadId, artist, null, filter, replaceExistingFiles, downloadClientItem, directoryInfo.Name, audioFiles, null));
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<ManualImportItem> ProcessFolder(string downloadId, Artist overrideArtist, Album overrideAlbum, FilterFilesType filter, bool replaceExistingFiles, DownloadClientItem downloadClientItem, string albumTitle, List<IFileInfo> audioFiles, string cuesheetPath)
|
||||
private List<ManualImportItem> ProcessFolder(string downloadId, Artist overrideArtist, Album overrideAlbum, FilterFilesType filter, bool replaceExistingFiles, DownloadClientItem downloadClientItem, string albumTitle, List<IFileInfo> audioFiles, CueSheet cueSheet)
|
||||
{
|
||||
var idOverrides = new IdentificationOverrides
|
||||
{
|
||||
|
@ -200,7 +209,8 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
|||
{
|
||||
DownloadClientItem = downloadClientItem,
|
||||
ParsedAlbumInfo = Parser.Parser.ParseAlbumTitle(albumTitle),
|
||||
IsSingleFileRelease = !cuesheetPath.Empty()
|
||||
CueSheet = cueSheet,
|
||||
IsSingleFileRelease = cueSheet != null ? cueSheet.IsSingleFileRelease : false,
|
||||
};
|
||||
var config = new ImportDecisionMakerConfig
|
||||
{
|
||||
|
@ -225,7 +235,10 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
|||
var existingItems = existingDecisions.Select(x => MapItem(x, null, replaceExistingFiles, false));
|
||||
|
||||
var itemsList = newItems.Concat(existingItems).ToList();
|
||||
itemsList.ForEach(item => { item.CuesheetPath = cuesheetPath; });
|
||||
if (cueSheet != null)
|
||||
{
|
||||
itemsList.ForEach(item => { item.CueSheetPath = cueSheet.Path; });
|
||||
}
|
||||
|
||||
return itemsList;
|
||||
}
|
||||
|
@ -265,7 +278,7 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
|||
IsSingleFileRelease = group.All(x => x.IsSingleFileRelease == true)
|
||||
};
|
||||
|
||||
// TODO support with the cuesheet
|
||||
// TODO support with the cue sheet
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(files, idOverride, itemInfo, config);
|
||||
|
||||
var existingItems = group.Join(decisions,
|
||||
|
@ -353,7 +366,7 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
|||
item.ReplaceExistingFiles = replaceExistingFiles;
|
||||
item.DisableReleaseSwitching = disableReleaseSwitching;
|
||||
item.IsSingleFileRelease = decision.Item.IsSingleFileRelease;
|
||||
item.CuesheetPath = decision.Item.CuesheetPath;
|
||||
item.CueSheetPath = decision.Item.CueSheetPath;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
@ -403,7 +416,7 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
|||
Album = album,
|
||||
Release = release,
|
||||
IsSingleFileRelease = file.IsSingleFileRelease,
|
||||
CuesheetPath = file.CuesheetPath,
|
||||
CueSheetPath = file.CueSheetPath,
|
||||
};
|
||||
|
||||
if (file.IsSingleFileRelease)
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace NzbDrone.Core.Parser.Model
|
|||
public string ReleaseGroup { get; set; }
|
||||
public string SceneName { get; set; }
|
||||
public bool IsSingleFileRelease { get; set; }
|
||||
public string CuesheetPath { get; set; }
|
||||
public string CueSheetPath { get; set; }
|
||||
public override string ToString()
|
||||
{
|
||||
return Path;
|
||||
|
|
Loading…
Reference in New Issue