Skip to content

Instantly share code, notes, and snippets.

@flashwave
Created January 18, 2021 19:05
Show Gist options
  • Save flashwave/9c39d6e9542f366818ac936fbe1a8d6a to your computer and use it in GitHub Desktop.
Save flashwave/9c39d6e9542f366818ac936fbe1a8d6a to your computer and use it in GitHub Desktop.
music man
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
namespace MusicManager {
public class Playlist : IDisposable, IEnumerable<string> {
private Stream Stream { get; }
private Mutex Lock { get; } = new Mutex();
private List<string> Paths { get; } = new List<string>();
public Playlist(string filename) : this(new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read)) { }
public Playlist(Stream stream) {
Stream = stream ?? throw new ArgumentNullException(nameof(stream));
}
public void Add(string path) {
Lock.WaitOne();
if(!Paths.Contains(path))
Paths.Add(path);
Lock.ReleaseMutex();
}
public bool Contains(string path) {
Lock.WaitOne();
bool contains = Paths.Contains(path);
Lock.ReleaseMutex();
return contains;
}
public void Load(bool allowGone = false) {
Lock.WaitOne();
Stream.Seek(0, SeekOrigin.Begin);
using StreamReader sr = new StreamReader(Stream, new UTF8Encoding(false), leaveOpen: true);
string line = sr.ReadLine();
if(line != null && line.StartsWith(@"#")) { // YEAH FILE FORMATS
while((line = sr.ReadLine()) != null)
if(allowGone || File.Exists(line))
Paths.Add(line);
}
Lock.ReleaseMutex();
}
public void Save() {
Lock.WaitOne();
Stream.SetLength(0);
using StreamWriter sw = new StreamWriter(Stream, new UTF8Encoding(false), leaveOpen: true);
sw.WriteLine(@"#");
foreach(string path in Paths)
sw.WriteLine(path);
Lock.ReleaseMutex();
}
private bool IsDisposed;
~Playlist()
=> Dispose(false);
public void Dispose()
=> Dispose(true);
private void Dispose(bool disposing) {
if(IsDisposed)
return;
IsDisposed = true;
Stream.Dispose();
Lock.Dispose();
if(disposing)
GC.SuppressFinalize(this);
}
public string this[int index] {
get {
string str;
Lock.WaitOne();
try {
str = Paths[index];
} finally {
Lock.ReleaseMutex();
}
return str;
}
}
public int Count
=> Paths.Count;
public IEnumerator<string> GetEnumerator()
=> new PlaylistEnumerator(this);
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
}
public class PlaylistEnumerator : IEnumerator<string> {
public string Current => Playlist[CurrentIndex];
object IEnumerator.Current => Current;
private int CurrentIndex { get; set; } = -1;
private Playlist Playlist { get; }
public PlaylistEnumerator(Playlist playlist) {
Playlist = playlist ?? throw new ArgumentNullException(nameof(playlist));
}
public bool MoveNext() {
return ++CurrentIndex < Playlist.Count;
}
public void Reset() {
CurrentIndex = -1;
}
public void Dispose() {
GC.SuppressFinalize(this);
}
}
}
using System;
using System.IO;
using static System.Console;
namespace MusicManager {
public static class Program {
private const string PREV_FILE = @"prev.m3u8";
private const string PREV_FILE_OLD = @"prev-{0}.m3u8";
private const string LIST_FILE = @"list.m3u8";
private const string SCAN_FILE = @"scan.txt";
private const string FINAL_FILE = @"final.m3u8";
public static void Main(string[] args) {
WriteLine(@"MusicManager");
WriteLine(@"Doing housekeeping...");
if(File.Exists(FINAL_FILE))
File.Delete(FINAL_FILE);
if(File.Exists(PREV_FILE))
File.Move(PREV_FILE, string.Format(PREV_FILE_OLD, DateTimeOffset.Now.ToUnixTimeSeconds()));
if(File.Exists(LIST_FILE))
File.Move(LIST_FILE, PREV_FILE);
WriteLine(@"Constructing new playlist according to the scanfile...");
using ScanFile scan = new ScanFile(SCAN_FILE);
using Playlist pl = new Playlist(LIST_FILE);
foreach(ScanFileDirective sfd in scan) {
foreach(string file in sfd.GetFiles()) {
pl.Add(file);
}
}
pl.Save();
if(File.Exists(PREV_FILE)) {
using Playlist prev = new Playlist(PREV_FILE);
using Playlist final = new Playlist(FINAL_FILE);
WriteLine(@"Loading previous playlist...");
prev.Load();
WriteLine(@"Filtering existing songs...");
foreach(string path in pl)
if(!prev.Contains(path))
final.Add(path);
WriteLine(@"Saving finalised playlist...");
final.Save();
} else {
WriteLine(@"No previous playlist is present, copying list to final.");
File.Copy(LIST_FILE, FINAL_FILE);
}
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
namespace MusicManager {
public class ScanFile : IDisposable, IEnumerable<ScanFileDirective> {
private Stream Stream { get; }
private Mutex Lock { get; } = new Mutex();
private List<ScanFileDirective> Directives { get; } = new List<ScanFileDirective>();
public ScanFile(string filename) : this(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) { Load(); }
public ScanFile(Stream stream) {
Stream = stream ?? throw new ArgumentNullException(nameof(stream));
}
public void Load() {
Lock.WaitOne();
using StreamReader sr = new StreamReader(Stream, new UTF8Encoding(false), leaveOpen: true);
string str;
Directives.Clear();
while((str = sr.ReadLine()) != null)
Directives.Add(new ScanFileDirective(str));
Lock.ReleaseMutex();
}
private bool IsDisposed;
~ScanFile()
=> Dispose(false);
public void Dispose()
=> Dispose(true);
private void Dispose(bool disposing) {
if(IsDisposed)
return;
IsDisposed = true;
Stream.Dispose();
Lock.Dispose();
if(disposing)
GC.SuppressFinalize(this);
}
public ScanFileDirective this[int index] {
get {
ScanFileDirective sfd;
Lock.WaitOne();
try {
sfd = Directives[index];
} finally {
Lock.ReleaseMutex();
}
return sfd;
}
}
public int Count
=> Directives.Count;
public IEnumerator<ScanFileDirective> GetEnumerator()
=> new ScanFileEnumerator(this);
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
}
public class ScanFileDirective {
public string Directive { get; }
public ScanFileDirective(string directive) {
Directive = directive ?? throw new ArgumentNullException(nameof(directive));
}
private static readonly string[] Extensions = new[] {
@".flac", @".mp3", @".ogg", @".m4a",
};
private static IEnumerable<string> RecursiveFiles(string directory) {
List<string> files = new List<string>();
foreach(string dir in Directory.GetDirectories(directory))
files.AddRange(RecursiveFiles(dir));
string[] cueFiles = Directory.GetFiles(directory, @"*.cue");
//if(cueFiles.Length == 1)
// files.Add(cueFiles.First());
//else
foreach(string file in Directory.GetFiles(directory)) {
if(Extensions.Contains(Path.GetExtension(file)))
files.Add(file);
}
return files;
}
public IEnumerable<string> GetFiles() {
if(Directory.Exists(Directive))
return RecursiveFiles(Directive);
if(File.Exists(Directive))
return new[] { Directive };
return Enumerable.Empty<string>();
}
}
public class ScanFileEnumerator : IEnumerator<ScanFileDirective> {
public ScanFileDirective Current => ScanFile[CurrentIndex];
object IEnumerator.Current => Current;
private int CurrentIndex { get; set; } = -1;
private ScanFile ScanFile { get; }
public ScanFileEnumerator(ScanFile scanFile) {
ScanFile = scanFile ?? throw new ArgumentNullException(nameof(scanFile));
}
public bool MoveNext() {
return ++CurrentIndex < ScanFile.Count;
}
public void Reset() {
CurrentIndex = -1;
}
public void Dispose() {
GC.SuppressFinalize(this);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment