1
0
Fork 0
mirror of https://github.com/fiso64/slsk-batchdl.git synced 2024-12-22 14:32:40 +00:00
slsk-batchdl/slsk-batchdl/M3uEditor.cs
2024-09-02 22:06:07 +02:00

338 lines
No EOL
10 KiB
C#

using Data;
using Enums;
using System.Text;
public class M3uEditor
{
public string path { get; private set; }
public M3uOption option = M3uOption.Index;
string parent;
List<string> lines;
bool needFirstUpdate = false;
readonly TrackLists trackLists;
readonly Dictionary<string, Track> previousRunData = new(); // { track.ToKey(), track }
public M3uEditor(TrackLists trackLists, M3uOption option)
{
this.trackLists = trackLists;
this.option = option;
this.needFirstUpdate = option == M3uOption.All;
}
public M3uEditor(string path, TrackLists trackLists, M3uOption option) : this(trackLists, option)
{
SetPathAndLoad(path);
}
public void SetPathAndLoad(string path)
{
if (this.path != null && Utils.NormalizedPath(this.path) == Utils.NormalizedPath(path))
return;
this.path = Path.GetFullPath(path);
parent = Utils.NormalizedPath(Path.GetDirectoryName(this.path));
lines = ReadAllLines().ToList();
LoadPreviousResults();
}
private void LoadPreviousResults()
{
// Format:
// #SLDL:<trackinfo>;<trackinfo>; ...
// where <trackinfo> = filepath,artist,album,title,length(int),tracktype(int),state(int),failurereason(int)
if (lines.Count == 0 || !lines[0].StartsWith("#SLDL:"))
return;
string sldlLine = lines[0];
lines = lines.Skip(1).ToList();
int k = "#SLDL:".Length;
var currentItem = new StringBuilder();
bool inQuotes = false;
for (; k < sldlLine.Length && sldlLine[k] == ' '; k++);
for (; k < sldlLine.Length; k++)
{
var track = new Track();
int field = 0;
for (int i = k; i < sldlLine.Length; i++)
{
char c = sldlLine[i];
if (c == '"' && (i == k || sldlLine[i - 1] != '\\'))
{
if (inQuotes && i + 1 < sldlLine.Length && sldlLine[i + 1] == '"')
{
currentItem.Append('"');
i++;
}
else
{
inQuotes = !inQuotes;
}
}
else if (field <= 6 && c == ',' && !inQuotes)
{
var x = currentItem.ToString();
if (field == 0)
{
if (x.StartsWith("./"))
x = Path.Join(parent, x[2..]);
track.DownloadPath = x;
}
else if (field == 1)
track.Artist = x;
else if (field == 2)
track.Album = x;
else if (field == 3)
track.Title = x;
else if (field == 4)
track.Length = int.Parse(x);
else if (field == 5)
track.Type = (TrackType)int.Parse(currentItem.ToString());
else if (field == 6)
track.State = (TrackState)int.Parse(x);
currentItem.Clear();
field++;
}
else if (field == 7 && c == ';')
{
track.FailureReason = (FailureReason)int.Parse(currentItem.ToString());
currentItem.Clear();
k = i;
break;
}
else
{
currentItem.Append(c);
}
}
previousRunData[track.ToKey()] = track;
}
}
public void Update()
{
if (option == M3uOption.None)
return;
lock (trackLists)
{
bool needUpdate = false;
int index = 1;
bool updateLine(string newLine)
{
bool changed = index >= lines.Count || newLine != lines[index];
while (index >= lines.Count) lines.Add("");
lines[index] = newLine;
return changed;
}
bool trackChanged(Track track, Track? indexTrack)
{
return indexTrack == null
|| indexTrack.State != track.State
|| indexTrack.FailureReason != track.FailureReason
|| Utils.NormalizedPath(indexTrack.DownloadPath) != Utils.NormalizedPath(track.DownloadPath);
}
void updateTrackIfNeeded(Track track)
{
var key = track.ToKey();
previousRunData.TryGetValue(key, out Track? indexTrack);
if (!needUpdate)
needUpdate = trackChanged(track, indexTrack);
if (needUpdate)
{
if (indexTrack == null)
previousRunData[key] = new Track(track);
else
{
indexTrack.State = track.State;
indexTrack.FailureReason = track.FailureReason;
indexTrack.DownloadPath = track.DownloadPath;
}
}
}
foreach (var tle in trackLists.lists)
{
if (tle.source.Type != TrackType.Normal)
{
if (tle.source.State != TrackState.Initial)
{
updateTrackIfNeeded(tle.source);
}
}
for (int k = 0; k < tle.list.Count; k++)
{
for (int j = 0; j < tle.list[k].Count; j++)
{
var track = tle.list[k][j];
if (track.IsNotAudio || track.State == TrackState.Initial)
continue;
updateTrackIfNeeded(track);
if (option == M3uOption.All)
{
needUpdate |= updateLine(TrackToLine(track));
index++;
}
}
}
}
if (needUpdate || needFirstUpdate)
{
needFirstUpdate = false;
WriteAllLines();
}
}
}
private void WriteAllLines()
{
if (!Directory.Exists(parent))
Directory.CreateDirectory(parent);
using var fileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
using var writer = new StreamWriter(fileStream);
WriteSldlLine(writer);
foreach (var line in lines)
{
writer.Write(line);
writer.Write('\n');
}
}
private void WriteSldlLine(StreamWriter writer)
{
// Format:
// #SLDL:<trackinfo>;<trackinfo>; ...
// where <trackinfo> = filepath,artist,album,title,length(int),tracktype(int),state(int),failurereason(int)
void writeCsvLine(string[] items)
{
bool comma = false;
foreach (var item in items)
{
if (comma)
writer.Write(',');
if (item.Contains(',') || item.Contains('\"'))
{
writer.Write('"');
writer.Write(item.Replace("\"", "\"\""));
writer.Write('"');
}
else
{
writer.Write(item);
}
comma = true;
}
}
writer.Write("#SLDL:");
foreach (var val in previousRunData.Values)
{
string p = val.DownloadPath;
if (Utils.NormalizedPath(p).StartsWith(parent))
p = "./" + System.IO.Path.GetRelativePath(parent, p); // prepend ./ for LoadPreviousResults to recognize that a rel. path is used
var items = new string[]
{
p,
val.Artist,
val.Album,
val.Title,
val.Length.ToString(),
((int)val.Type).ToString(),
((int)val.State).ToString(),
((int)val.FailureReason).ToString(),
};
writeCsvLine(items);
writer.Write(';');
}
writer.Write('\n');
}
private string TrackToLine(Track track)
{
string? failureReason = track.FailureReason != FailureReason.None ? track.FailureReason.ToString() : null;
if (failureReason == null && track.State == TrackState.NotFoundLastTime)
failureReason = nameof(FailureReason.NoSuitableFileFound);
if (failureReason != null)
return $"#FAIL: {track} [{failureReason}]";
if (track.DownloadPath.Length > 0)
{
if (Utils.NormalizedPath(track.DownloadPath).StartsWith(parent))
return Path.GetRelativePath(parent, track.DownloadPath);
else
return track.DownloadPath;
}
return $"# {track}";
}
public Track? PreviousRunResult(Track track)
{
var key = track.ToKey();
previousRunData.TryGetValue(track.ToKey(), out var t);
return t;
}
public bool TryGetPreviousRunResult(Track track, out Track? result)
{
previousRunData.TryGetValue(track.ToKey(), out result);
return result != null;
}
public bool TryGetFailureReason(Track track, out FailureReason reason)
{
reason = FailureReason.None;
var t = PreviousRunResult(track);
if (t != null && t.State == TrackState.Failed)
{
reason = t.FailureReason;
return true;
}
return false;
}
private string ReadAllText()
{
if (!File.Exists(path))
return "";
using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using var streamReader = new StreamReader(fileStream);
return streamReader.ReadToEnd();
}
private string[] ReadAllLines()
{
return ReadAllText().TrimEnd().Split('\n');
}
}