Example #1
0
        static int RunProgram(string[] args)
        {
            //check to make sure we have at least one downloadInfo to run
            if (DownloadInfos.Count == 0)
            {
                WriteToLog("No DownloadInfos parsed! (empty xml file?)");
                if (!NoErrorPrompts)
                {
                    Console.ReadLine();
                }
                Environment.Exit(-1);
            }

            //check to make sure at least one downloadInfo enabled
            List <DownloadInfo> enabledInfos = DownloadInfos.Where(info => info.Enabled).ToList();

            if (enabledInfos.Count == 0)
            {
                WriteToLog("No DownloadInfos enabled!");
                if (!NoErrorPrompts)
                {
                    Console.ReadLine();
                }
                Environment.Exit(-1);
            }

            //only process enabled infos
            DownloadInfos = enabledInfos;

            //if not silent, add start of application here
            if (!NoPrompts)
            {
                WriteToLog("Press enter to start");
                //https://stackoverflow.com/questions/11512821/how-to-stop-c-sharp-console-applications-from-closing-automatically
                Console.ReadLine();
            }

            //run an update on youtube-dl
            //https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process?redirectedfrom=MSDN&view=netframework-4.7.2
            if (!NoPrompts)
            {
                UpdateYoutubeDL = GetUserResponse("UpdateYoutubeDL?");
            }
            if (UpdateYoutubeDL)
            {
                WriteToLog("Update YoutubeDL");
                //create folder if it does not already exist
                CheckMissingFolder(BinaryFolder);

                //check if youtube-dl is missing, if so download it
                string youtubeDLPath = Path.Combine(BinaryFolder, YoutubeDL);
                if (!File.Exists(youtubeDLPath) || ForceDownloadYoutubeDl)
                {
                    WriteToLog("Youtube-dl.exe does not exist, or ForceDownloadYoutubeDl = true, download it");
                    try
                    {
                        using (WebClient client = new WebClient())
                        {
                            if (File.Exists(youtubeDLPath))
                            {
                                File.Delete(youtubeDLPath);
                            }
                            client.DownloadFile(YoutubeDlUrl, youtubeDLPath);
                        }
                    }
                    catch (WebException ex)
                    {
                        WriteToLog("Failed to download youtube-dl.exe");
                        WriteToLog(ex.ToString());
                        if (!NoErrorPrompts)
                        {
                            Console.ReadLine();
                        }
                        Environment.Exit(-1);
                    }
                }
                else
                {
                    WriteToLog("Youtube-dl.exe exists");

                    //try to launch youtube-dl to update youtube-dl
                    try
                    {
                        using (Process updateYoutubeDL = new Process())
                        {
                            //set properties
                            updateYoutubeDL.StartInfo.RedirectStandardError  = false;
                            updateYoutubeDL.StartInfo.RedirectStandardOutput = false;
                            updateYoutubeDL.StartInfo.UseShellExecute        = true;
                            updateYoutubeDL.StartInfo.WorkingDirectory       = BinaryFolder;
                            updateYoutubeDL.StartInfo.FileName       = YoutubeDL;
                            updateYoutubeDL.StartInfo.CreateNoWindow = false;
                            updateYoutubeDL.StartInfo.Arguments      = "--update";
                            updateYoutubeDL.Start();
                            updateYoutubeDL.WaitForExit();
                            if (updateYoutubeDL.ExitCode != 0)
                            {
                                WriteToLog(string.Format("ERROR: update process exited with code {0}", updateYoutubeDL.ExitCode));
                                if (!NoErrorPrompts)
                                {
                                    Console.ReadLine();
                                }
                                Environment.Exit(-1);
                            }
                            System.Threading.Thread.Sleep(TimeSpan.FromSeconds(10.0));
                        }
                    }
                    catch (Exception e)
                    {
                        WriteToLog(e.ToString());
                        if (!NoErrorPrompts)
                        {
                            Console.ReadLine();
                        }
                        Environment.Exit(-1);
                    }
                }
            }
            else
            {
                WriteToLog("UpdateYoutubeDl skipped");
            }

            if (!NoPrompts)
            {
                CopyBinaries = GetUserResponse("CopyBinaries?");
            }
            if (CopyBinaries)
            {
                WriteToLog("Copy Binaries");
                CheckMissingFolder(BinaryFolder);

                //if embedded binary does not exist, OR force binary embedded write, then write it
                string[] exesToGet = { AtomicParsley, CommandShellWrapper };
                foreach (string exeToGet in exesToGet)
                {
                    string binaryPath = Path.Combine(BinaryFolder, exeToGet);
                    if (!File.Exists(binaryPath) || ForceWriteFFBinaries)
                    {
                        WriteToLog(string.Format("File {0} does not exist or ForceWriteFFBinaries is on, writing binaries to disk", exeToGet));
                        File.WriteAllBytes(binaryPath, GetEmbeddedResource(Path.GetFileNameWithoutExtension(exeToGet)));
                    }
                }

                string[] zipsToGet = { "ffmpeg.zip", "ffprobe.zip" };
                foreach (string zipToGet in zipsToGet)
                {
                    string binaryPath = Path.Combine(BinaryFolder, string.Format("{0}.{1}", Path.GetFileNameWithoutExtension(zipToGet), "exe"));
                    if (!File.Exists(binaryPath) || ForceWriteFFBinaries)
                    {
                        WriteToLog(string.Format("File {0} does not exist or ForceWriteFFBinaries is on, writing binaries to disk", zipToGet));
                        using (Stream stream = GetEmbeddedResourceStream(zipToGet))
                            using (ZipFile zip = ZipFile.Read(stream))
                            {
                                zip.ExtractAll(BinaryFolder, ExtractExistingFileAction.OverwriteSilently);
                            }
                    }
                }

                //copy the binaries to each folder
                foreach (DownloadInfo info in DownloadInfos.Where(temp => temp.DownloadType == DownloadType.YoutubeSong || temp.DownloadType == DownloadType.YoutubeMix))
                {
                    CheckMissingFolder(info.Folder);
                    foreach (string binaryFile in BinaryFiles)
                    {
                        WriteToLog(string.Format("Copying file {0} into folder {1}", binaryFile, info.Folder));
                        string fileToCopy = Path.Combine(info.Folder, binaryFile);
                        if (File.Exists(fileToCopy))
                        {
                            File.Delete(fileToCopy);
                        }
                        File.Copy(Path.Combine(BinaryFolder, binaryFile), fileToCopy);
                    }
                }
            }
            else
            {
                WriteToLog("CopyBinaries skipped");
            }

            //ask user if we will run the scripts
            if (!NoPrompts)
            {
                RunScripts = GetUserResponse("RunScripts?");
            }
            if (RunScripts)
            {
                WriteToLog("Running scripts");

                //build and run the process list for youtube downloads
                //build first
                List <Process> processes = new List <Process>();

                //only create processes for youtube download types
                foreach (DownloadInfo info in DownloadInfos.Where(temp => temp.DownloadType == DownloadType.YoutubeSong || temp.DownloadType == DownloadType.YoutubeMix))
                {
                    //make sure folder path exists
                    CheckMissingFolder(info.Folder);

                    //make sure required binaries exist
                    foreach (string binaryFile in BinaryFiles)
                    {
                        CheckMissingFile(Path.Combine(info.Folder, binaryFile));
                    }

                    //delete any previous song file entries
                    foreach (string file in Directory.GetFiles(info.Folder, "*", SearchOption.TopDirectoryOnly).Where(file => ValidExtensions.Contains(Path.GetExtension(file))))
                    {
                        WriteToLog("Deleting old song file from previous run: " + Path.GetFileName(file));
                        File.Delete(file);
                    }

                    WriteToLog(string.Format("Build process info folder {0}", info.Folder));
                    processes.Add(new Process()
                    {
                        StartInfo = new ProcessStartInfo()
                        {
                            RedirectStandardError  = false,
                            RedirectStandardOutput = false,
                            UseShellExecute        = true,
                            WorkingDirectory       = info.Folder,
                            FileName       = CommandShellWrapper,
                            CreateNoWindow = false,
                            Arguments      = YoutubeDL + " " + string.Format(DefaultCommandLine,                                                                                            //from xml
                                                                             info.FirstRun ? string.Empty : DateAfterCommandLine,                                                           //date after (youtube-dl command line key)
                                                                             info.FirstRun ? string.Empty : info.LastDate,                                                                  //date after (youtube-dl command line arg)
                                                                             info.DownloadType == DownloadType.YoutubeMix ? YoutubeMixDurationCommandLine : YoutubeSongDurationCommandLine, //youtube-dl match filter duration selector
                                                                             string.IsNullOrEmpty(info.CustomYoutubedlCommands) ? string.Empty : info.CustomYoutubedlCommands,
                                                                             info.DownloadURL)
                        }
                    });
                }

                //run them all now
                foreach (Process p in processes)
                {
                    try
                    {
                        WriteToLog(string.Format("Launching process for folder {0} using arguments {1}", p.StartInfo.WorkingDirectory, p.StartInfo.Arguments));
                        p.Start();
                    }
                    catch (Exception ex)
                    {
                        WriteToLog("An error has occurred running a process, stopping all");
                        KillProcesses(processes);
                        WriteToLog(ex.ToString());
                        if (!NoErrorPrompts)
                        {
                            Console.ReadLine();
                        }
                        Environment.Exit(-1);
                    }
                }

                //iterate to wait for all to complete
                foreach (Process p in processes)
                {
                    p.WaitForExit();
                    WriteToLog(string.Format("Process of folder {0} has finished or previously finished. exit code {1}", p.StartInfo.WorkingDirectory, p.ExitCode));
                }

                //check exit code status
                bool forceExit = false;
                foreach (Process p in processes)
                {
                    WriteToLog(string.Format("Process of folder {0} exited of code {1}", p.StartInfo.WorkingDirectory, p.ExitCode));
                    if (p.ExitCode == 0 || p.ExitCode == 1)
                    {
                        WriteToLog("Valid exit code");
                    }
                    else
                    {
                        WriteToLog("Invalid exit code, marked to exit");
                        forceExit = true;
                    }
                }

                //determine if to quit
                if (forceExit)
                {
                    WriteToLog("An exit code above was bad, exiting");
                    KillProcesses(processes);
                    if (!NoErrorPrompts)
                    {
                        Console.ReadLine();
                    }
                    Environment.Exit(-1);
                }

                //after all completed successfully, dispose of them
                foreach (Process p in processes)
                {
                    p.Dispose();
                }
                GC.Collect();
                WriteToLog("All processes completed");

                //check if creating archive text files
                foreach (DownloadInfo info in DownloadInfos.FindAll(temp => (temp.DownloadType == DownloadType.YoutubeSong || temp.DownloadType == DownloadType.YoutubeMix) && (!string.IsNullOrWhiteSpace(temp.CreateArchive)) && (temp.Enabled)))
                {
                    WriteToLog(string.Format("Creating archive file for folder {0}, called {1}", info.Folder, info.CreateArchive));

                    //read the output log of the first run
                    string outputLogPath = Path.Combine(info.Folder, CommandLineWrapperLogfile);
                    if (!File.Exists(outputLogPath))
                    {
                        WriteToLog("Output log does not exist for folder " + info.Folder);
                        if (!NoErrorPrompts)
                        {
                            Console.ReadLine();
                        }
                        Environment.Exit(-1);
                    }
                    string[] youtubeDlLog = File.ReadAllLines(outputLogPath);

                    //do a regex search for each video url
                    List <string> archiveLines = new List <string>();
                    foreach (string line in youtubeDlLog)
                    {
                        Match result = Regex.Match(line, CreateArchiveRegex);
                        if (result.Success)
                        {
                            string valueToAppend = result.Value;
                            WriteToLog(string.Format("Regex match: {0}", valueToAppend));

                            //this gives you two results:
                            //[youtube] gQjAEbWZEgU:
                            //[youtube] HdOCXC8ZwAw: Writing thumbnail to:

                            //array split on ":" character
                            valueToAppend = valueToAppend.Split(':')[0];

                            //remove brackets for [youtube] -> youtube
                            valueToAppend = valueToAppend.Replace(@"[youtube]", @"youtube");

                            //add to list
                            if (!archiveLines.Contains(valueToAppend))
                            {
                                archiveLines.Add(valueToAppend);
                            }
                        }
                    }

                    //and write to text
                    if (archiveLines.Count > 0)
                    {
                        archiveLines.Add(string.Empty);
                        File.WriteAllLines(Path.Combine(info.Folder, info.CreateArchive), archiveLines);
                        WriteToLog(string.Format("{0} written for folder {1}", info.CreateArchive, info.Folder));
                    }

                    //also turn off creating archive, it should only be a one time thing
                    info.CreateArchive = string.Empty;
                    UpdateDownloadInfoXmlEntry(doc, info, nameof(info.CreateArchive), info.CreateArchive, false);
                }
            }
            else
            {
                WriteToLog("RunScripts skipped");
            }

            //save the new date for when the above scripts were last run
            if (!NoPrompts)
            {
                SaveNewDate = GetUserResponse("SaveNewDate?");
            }
            if (SaveNewDate)
            {
                WriteToLog("Saving new date");

                //https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings
                string newDate = string.Format("{0:yyyyMMdd}", DateTime.Now);
                for (int i = 0; i < DownloadInfos.Count; i++)
                {
                    WriteToLog(string.Format("Changing and saving xml old date from {0} to {1}", DownloadInfos[i].LastDate, newDate));
                    DownloadInfos[i].LastDate = newDate;

                    //then save it in xml
                    UpdateDownloadInfoXmlEntry(doc, DownloadInfos[i], "LastDate", DownloadInfos[i].LastDate, true);
                }
            }
            else
            {
                WriteToLog("SaveNewDate skipped");
            }

            //start naming and tagging
            //get a list of files from the directory listed
            if (!NoPrompts)
            {
                ParseTags = GetUserResponse("ParseTags?");
            }
            if (ParseTags)
            {
                WriteToLog("Parsing tags");
                for (int j = 0; j < DownloadInfos.Count; j++)
                {
                    DownloadInfo info = DownloadInfos[j];

                    //save the track number in a backup in case if copying is true, and there's a "same title with different track number" error
                    //if that happends, the old number needs to be written back to disk because the whole naming process is not invalid
                    info.BackupLastTrackNumber = info.LastTrackNumber;
                    WriteToLog("");
                    WriteToLog("-----------------------Parsing directory " + info.Folder + "----------------------");
                    CheckMissingFolder(info.Folder);

                    //make and filter out the lists
                    List <string> files = Directory.GetFiles(info.Folder, "*", SearchOption.TopDirectoryOnly).Where(file => ValidExtensions.Contains(Path.GetExtension(file))).ToList();

                    //check to make sure there are valid audio files before proceding
                    if (files.Count == 0)
                    {
                        WriteToLog("No valid audio files in directory");
                        continue;
                    }

                    //step 0: check for if padding is needed
                    //get the number of track numbers for the first and last files
                    int firstEntryNumTrackNums = Path.GetFileName(files[0]).Split('-')[0].Length;
                    int maxEntryNumTrackNums   = 0;
                    foreach (string s in files)
                    {
                        string filename = Path.GetFileName(s);
                        int    currentEntryNumTrackNums = filename.Split('-')[0].Length;
                        if (currentEntryNumTrackNums > maxEntryNumTrackNums)
                        {
                            maxEntryNumTrackNums = currentEntryNumTrackNums;
                        }
                    }

                    WriteToLog(string.Format("First entry, track number padding = {0}\nmax entry, track number padding = {1}\n",
                                             firstEntryNumTrackNums, maxEntryNumTrackNums));
                    if (firstEntryNumTrackNums != maxEntryNumTrackNums)
                    {
                        //inform and ask
                        WriteToLog("Not equal, padding entries");
                        //use the last entry as reference point for how many paddings to do
                        for (int i = 0; i < files.Count; i++)
                        {
                            string oldFileName = Path.GetFileName(files[i]);
                            int    numToPadOut = maxEntryNumTrackNums - oldFileName.Split('-')[0].Length;
                            if (numToPadOut > 0)
                            {
                                string newFileName = oldFileName.PadLeft(oldFileName.Length + numToPadOut, '0');
                                WriteToLog(string.Format("{0}\nrenamed to\n{1}", oldFileName, newFileName));
                                File.Move(Path.Combine(info.Folder, oldFileName), Path.Combine(info.Folder, newFileName));
                                files[i] = Path.Combine(info.Folder, newFileName);
                            }
                            else
                            {
                                WriteToLog(string.Format("{0}\nnot renamed", oldFileName));
                            }
                        }
                        //and re-sort if afterwards
                        files.Sort();
                    }

                    //step 1: parse the tag info
                    for (int i = 0; i < files.Count; i++)
                    {
                        bool   fileDeleted = false;
                        bool   skipFile    = false;
                        string fileName    = files[i];
                        WriteToLog("Parsing " + fileName);

                        //create TagLib instance
                        TagLib.Tag  tag  = null;
                        TagLib.File file = null;
                        try
                        {
                            //https://stackoverflow.com/questions/40826094/how-do-i-use-taglib-sharp
                            file = TagLib.File.Create(fileName);
                            tag  = file.Tag;
                        }
                        catch (Exception ex)
                        {
                            WriteToLog(ex.ToString());
                            if (!NoErrorPrompts)
                            {
                                Console.ReadLine();
                            }
                            Environment.Exit(-1);
                        }

                        //assign tag infos from xml properties
                        //album
                        tag.Album = info.Album;

                        //album artist
                        //https://stackoverflow.com/questions/17292142/taglib-sharp-not-editing-artist
                        if (!string.IsNullOrEmpty(info.AlbumArtist))
                        {
                            tag.AlbumArtists = null;
                            tag.AlbumArtists = new string[] { info.AlbumArtist };
                        }

                        //track number
                        //last saved number in the xml will be the last track number applied
                        //so up it first, then use it
                        tag.Track = ++info.LastTrackNumber;

                        //genre
                        tag.Genres = null;
                        tag.Genres = new string[] { info.Genre };

                        if (info.DownloadType == DownloadType.Other1)
                        {
                            //Other1 is mixes, add current year and artist as VA
                            tag.Performers = null;
                            tag.Performers = new string[] { "VA" };
                            tag.Year       = (uint)DateTime.Now.Year;
                            WriteToLog("Song treated as heartAtThis mix");
                        }
                        else//youtube mix and song
                        {
                            //artist and title and year from filename
                            //get the name of the file to parse tag info to
                            string fileNameToParse = Path.GetFileNameWithoutExtension(fileName);
                            //replace "–" with "-", as well as "?-" with "-"
                            fileNameToParse = fileNameToParse.Replace('–', '-').Replace("?-", "-");
                            //split based on "--" unique separater
                            string[] splitFileName = fileNameToParse.Split(new string[] { "--" }, StringSplitOptions.RemoveEmptyEntries);
                            //if the count is 1, then there are no "--", implies a run in this directory already happened
                            if (splitFileName.Count() == 1)
                            {
                                WriteToLog(string.Format("File {0} seems to have already been parsed, skipping", splitFileName[0]));
                                //decrease the number, it's not a new song
                                info.LastTrackNumber--;
                                continue;
                            }

                            //split into mix parsing and song parsing
                            if (info.DownloadType == DownloadType.YoutubeMix)
                            {
                                //from youtube-dl output template:
                                //[0] = autonumber (discard), [1] = title (actual title), [2] = upload year
                                //title
                                tag.Title = splitFileName[1].Trim();

                                //year is YYYMMDD, only want YYYY
                                bool   validYear  = false;
                                string yearString = string.Empty;
                                yearString = splitFileName[2].Substring(0, 4).Trim();
                                //remove any extra "-" characters
                                yearString = yearString.Replace("-", string.Empty).Trim();
                                while (!validYear)
                                {
                                    try
                                    {
                                        tag.Year  = uint.Parse(yearString);
                                        validYear = true;
                                    }
                                    catch
                                    {
                                        Console.Beep(1000, 500);
                                        WriteToLog("ERROR: Invalid year, please manually type");
                                        WriteToLog("Original: " + yearString);
                                        WriteToLog("Enter new (ONLY \"YYYY\")");
                                        yearString = Console.ReadLine().Trim();
                                    }
                                }

                                //artist (is VA for mixes)
                                tag.Performers = null;
                                tag.Performers = new string[] { "VA" };
                                WriteToLog("Song treated as youtube mix");
                            }
                            else//youtube song
                            {
                                //from youtube-dl output template:
                                //[0] = autonumber (discard), [1] = title (artist and title), [2] = upload year

                                //first get the artist title combo from parsed filename form youtube-dl
                                string   filenameArtistTitle  = splitFileName[1].Trim();
                                string[] splitArtistTitleName = null;

                                bool validArtistTitleParse = false;
                                while (!validArtistTitleParse)
                                {
                                    //labels divide the artist and title by " - "
                                    //split into artist [0] and title [1]
                                    splitArtistTitleName = filenameArtistTitle.Split(new string[] { " - " }, StringSplitOptions.None);

                                    //should be 2 entries for this to work
                                    if (splitArtistTitleName.Count() < 2)
                                    {
                                        Console.Beep(1000, 500);
                                        WriteToLog("ERROR: not enough split entries for parsing, please enter manually! (count is " + splitArtistTitleName.Count() + " )");
                                        WriteToLog("Original: " + filenameArtistTitle);
                                        WriteToLog("Enter new: (just arist - title combo)");
                                        WriteToLog("Or \"skip\" to remove song from parsing");
                                        filenameArtistTitle = Console.ReadLine().Trim();
                                        if (filenameArtistTitle.Equals("skip"))
                                        {
                                            skipFile = true;
                                            break;
                                        }
                                    }
                                    else
                                    {
                                        validArtistTitleParse = true;
                                    }
                                }

                                if (skipFile)
                                {
                                    filenameArtistTitle = "skipping - skipping--6969";
                                }

                                //get the artist name from split
                                tag.Performers = null;
                                tag.Performers = new string[] { splitArtistTitleName[0].Trim() };

                                //include anything after it rather than just get the last one, in case there's more split characters
                                //skip one for the artist name
                                tag.Title = string.Join(" - ", splitArtistTitleName.Skip(1)).Trim();

                                //year is YYYMMDD, only want YYYY
                                bool   validYear  = false;
                                string yearString = string.Empty;
                                yearString = splitFileName[2].Substring(0, 4).Trim();

                                //remove any extra "-" characters
                                yearString = yearString.Replace("-", string.Empty).Trim();
                                while (!validYear)
                                {
                                    try
                                    {
                                        tag.Year  = uint.Parse(yearString);
                                        validYear = true;
                                    }
                                    catch
                                    {
                                        Console.Beep(1000, 500);
                                        WriteToLog("ERROR: Invalid year, please manually type");
                                        WriteToLog("Original: " + yearString);
                                        WriteToLog("Enter new (ONLY \"YYYY\")");
                                        yearString = Console.ReadLine().Trim();
                                    }
                                }
                                WriteToLog("Song treated as youtube song");
                            }
                        }
                        if (skipFile)
                        {
                            WriteToLog(string.Format("Skipping song {0}", tag.Title));
                            File.Delete(fileName);

                            //also delete the entry from list of files to process
                            files.Remove(fileName);

                            //also put the counter back for track numbers
                            info.LastTrackNumber--;

                            //also decrement the counter as to not skip
                            i--;

                            //also note it
                            fileDeleted = true;
                        }
                        //check to make sure song doesn't already exist (but only if it's not the first time downloading
                        else if (!info.FirstRun)
                        {
                            if (SongAlreadyExists(info.CopyPaths, tag.Title))
                            {
                                WriteToLog(string.Format("WARNING: Song {0} already exists in a copy folder, deleting the entry!", tag.Title));
                                if (!NoPrompts)
                                {
                                    Console.ReadLine();
                                }
                                File.Delete(fileName);

                                //also delete the entry from list of files to process
                                files.Remove(fileName);

                                //also put the counter back for track numbers
                                info.LastTrackNumber--;

                                //also decrement the counter as to not skip
                                i--;

                                //also note it
                                fileDeleted = true;
                            }
                        }
                        if (!fileDeleted)
                        {
                            file.Save();
                        }
                    }

                    //step 2: parse the filenames from the tags
                    foreach (string fileName in files)
                    {
                        //load the file again
                        TagLib.File file = null;
                        try
                        {
                            //https://stackoverflow.com/questions/40826094/how-do-i-use-taglib-sharp
                            file = TagLib.File.Create(fileName);
                        }
                        catch (Exception ex)
                        {
                            WriteToLog(ex.ToString());
                            if (!NoErrorPrompts)
                            {
                                Console.ReadLine();
                            }
                            Environment.Exit(-1);
                        }

                        //get the old name
                        string oldFileName = Path.GetFileNameWithoutExtension(fileName);

                        //prepare the new name
                        string newFileName = string.Empty;

                        //manual check to make sure track and title exist
                        if (file.Tag.Track == 0)
                        {
                            WriteToLog(string.Format("ERROR: Track property is missing in file {0}", fileName));
                            if (NoErrorPrompts)
                            {
                                Environment.Exit(-1);
                            }
                            while (true)
                            {
                                WriteToLog("Please input unsigned int manually!");
                                if (uint.TryParse(Console.ReadLine(), out uint result))
                                {
                                    file.Tag.Track = result;
                                    file.Save();
                                    break;
                                }
                            }
                        }
                        if (string.IsNullOrWhiteSpace(file.Tag.Title))
                        {
                            WriteToLog(string.Format("ERROR: Title property is missing in file {0}", fileName));
                            if (NoErrorPrompts)
                            {
                                Environment.Exit(-1);
                            }
                            WriteToLog("Please input manually!");
                            file.Tag.Title = Console.ReadLine();
                            file.Save();
                        }
                        switch (info.DownloadType)
                        {
                        case DownloadType.Other1:
                            //using the pre-parsed title...
                            newFileName = string.Format("{0}-{1}", file.Tag.Track.ToString(), file.Tag.Title);
                            break;

                        case DownloadType.YoutubeMix:
                            newFileName = string.Format("{0}-{1}", file.Tag.Track.ToString(), file.Tag.Title);
                            break;

                        case DownloadType.YoutubeSong:
                            if (file.Tag.Performers == null || file.Tag.Performers.Count() == 0)
                            {
                                WriteToLog(string.Format("ERROR: Artist property is missing in file {0}", fileName));
                                if (NoErrorPrompts)
                                {
                                    Environment.Exit(-1);
                                }
                                WriteToLog("Please input manually!");
                                file.Tag.Performers = null;
                                file.Tag.Performers = new string[] { Console.ReadLine() };
                                file.Save();
                            }
                            newFileName = string.Format("{0}-{1} - {2}", file.Tag.Track.ToString(), file.Tag.Performers[0], file.Tag.Title);
                            break;

                        default:
                            WriteToLog("Invalid downloadtype: " + info.DownloadType.ToString());
                            if (!NoErrorPrompts)
                            {
                                Console.ReadLine();
                            }
                            continue;
                        }

                        //check for padding
                        //set padding to highest number of tracknumbers
                        //(if tracks go from 1-148, make sure filename for 1 is 001)
                        int trackPaddingLength       = newFileName.Split('-')[0].Length;
                        int maxTrackNumPaddingLength = info.LastTrackNumber.ToString().Length;
                        if (trackPaddingLength < maxTrackNumPaddingLength)
                        {
                            WriteToLog("Correcting for track padding");
                            int numToPad = maxTrackNumPaddingLength - trackPaddingLength;
                            newFileName = newFileName.PadLeft(newFileName.Length + numToPad, '0');
                        }

                        //save the complete folder path
                        string completeFolderPath = Path.GetDirectoryName(fileName);
                        string completeOldPath    = Path.Combine(completeFolderPath, oldFileName + Path.GetExtension(fileName));
                        string completeNewPath    = Path.Combine(completeFolderPath, newFileName + Path.GetExtension(fileName));
                        WriteToLog(string.Format("Renaming {0}\n                           to {1}", oldFileName, newFileName));
                        File.Move(completeOldPath, completeNewPath);
                    }

                    //also change firstRun to false if not done already
                    if (info.FirstRun)
                    {
                        WriteToLog(string.Format("DEBUG: folder {0} firstRun is true, setting to false", info.Folder));
                        info.FirstRun = false;
                    }

                    //at the end of each folder, write the new value back to the xml file
                    string  xpath    = string.Format("//DownloadInfo.xml/DownloadInfos/DownloadInfo[@Folder='{0}']", info.Folder);
                    XmlNode infoNode = doc.SelectSingleNode(xpath);

                    if (infoNode == null)
                    {
                        WriteToLog("Failed to select node for saving LastTrackNumber back to folder " + info.Folder);
                        if (!NoErrorPrompts)
                        {
                            Console.ReadLine();
                        }
                        Environment.Exit(-1);
                    }

                    XmlAttribute lastTrackNumber = infoNode.Attributes["LastTrackNumber"];
                    if (lastTrackNumber == null)
                    {
                        lastTrackNumber = doc.CreateAttribute("LastTrackNumber");
                        infoNode.Attributes.Append(lastTrackNumber);
                    }
                    lastTrackNumber.Value = info.LastTrackNumber.ToString();

                    infoNode.Attributes[nameof(info.FirstRun)].Value = info.FirstRun.ToString();
                    doc.Save(DownloadInfoXml);
                    WriteToLog("Saved LastTrackNumber for folder " + info.Folder);
                }
            }
            else
            {
                WriteToLog("ParseTags skipped");
            }

            //copy newly parsed files to their directories
            if (!NoPrompts)
            {
                CopyFiles = GetUserResponse("CopyFiles?");
            }
            if (CopyFiles)
            {
                WriteToLog("Copy Files");
                for (int j = 0; j < DownloadInfos.Count; j++)
                {
                    DownloadInfo info = DownloadInfos[j];
                    CheckMissingFolder(info.Folder);

                    //make and filter out the lists
                    List <string> files = Directory.GetFiles(info.Folder, "*", SearchOption.TopDirectoryOnly).Where(file => ValidExtensions.Contains(Path.GetExtension(file))).ToList();
                    WriteToLog("");
                    WriteToLog("-----------------------CopyFiles for directory " + info.Folder + "----------------------");
                    if (files.Count == 0)
                    {
                        WriteToLog("no files to copy");
                        continue;
                    }
                    bool breakout = false;

                    //using copy for all then delete because you can't move across drives (easily)
                    foreach (string copypath in info.CopyPaths)
                    {
                        WriteToLog("Copying files to directory " + copypath);
                        foreach (string file in files)
                        {
                            WriteToLog(Path.GetFileName(file));
                            string newPath = Path.Combine(copypath, Path.GetFileName(file));
                            if (File.Exists(newPath))
                            {
                                WriteToLog("WARNING: The file '{0}' already exists, this could indicate multiple copyTo targets! Skipping!");
                                if (!NoPrompts)
                                {
                                    Console.ReadLine();
                                }
                            }
                            else
                            {
                                File.Copy(file, newPath);
                            }
                        }
                    }
                    if (breakout)
                    {
                        continue;
                    }

                    //now delete
                    WriteToLog("Deleting files in infos folder");
                    foreach (string file in files)
                    {
                        if (File.Exists(file))
                        {
                            File.Delete(file);
                        }
                    }
                }
            }
            else
            {
                WriteToLog("CopyFiles skipped");
            }

            //delete the binaries in each folder
            if (!NoPrompts)
            {
                DeleteBinaries = GetUserResponse("Delete binaries?");
            }
            if (DeleteBinaries)
            {
                WriteToLog("Delete Binaries");

                foreach (DownloadInfo info in DownloadInfos.Where(temp => temp.DownloadType == DownloadType.YoutubeSong || temp.DownloadType == DownloadType.YoutubeMix))
                {
                    CheckMissingFolder(info.Folder);
                    foreach (string binaryFile in BinaryFiles)
                    {
                        WriteToLog(string.Format("Deleting file {0} in folder {1} if exist", binaryFile, info.Folder));

                        string fileToDelete = Path.Combine(info.Folder, binaryFile);
                        if (File.Exists(fileToDelete))
                        {
                            File.Delete(fileToDelete);
                        }
                    }
                }
            }
            else
            {
                WriteToLog("DeleteBinaries skipped");
            }

            //delete the output log files in each folder
            if (!NoPrompts)
            {
                DeleteOutputLogs = GetUserResponse("Delete youtube-dl output logs?");
            }
            if (DeleteOutputLogs)
            {
                WriteToLog("Delete youtube-dl output logs");

                foreach (DownloadInfo info in DownloadInfos.Where(temp => temp.DownloadType == DownloadType.YoutubeSong || temp.DownloadType == DownloadType.YoutubeMix))
                {
                    CheckMissingFolder(info.Folder);
                    WriteToLog(string.Format("Deleting youtube-dl log in folder {0} if exist", info.Folder));

                    string fileToDelete = Path.Combine(info.Folder, CommandLineWrapperLogfile);
                    if (File.Exists(fileToDelete))
                    {
                        File.Delete(fileToDelete);
                    }
                }
            }

            //and we're all set here
            WriteToLog("Done");
            if (!NoPrompts)
            {
                Console.ReadLine();
            }

            Environment.ExitCode = 0;
            Environment.Exit(0);
            return(0);
        }
Example #2
0
        //parse the xml file
        private static void ParseDownloadInfoXml()
        {
            //init tag parsing, load xml data
            //check to make sure download info xml file is present
            if (!File.Exists(DownloadInfoXml))
            {
                WriteToLog(string.Format("{0} is missing, application cannot continue", DownloadInfoXml));
                if (!NoErrorPrompts)
                {
                    Console.ReadLine();
                }
                Environment.Exit(-1);
            }
            WriteToLog("---------------------------APPLICATION START---------------------------");
            WriteToLog("Loading Xml document");
            doc = new XmlDocument();
            try
            {
                doc.PreserveWhitespace = true;
                doc.Load(DownloadInfoXml);
            }
            catch (XmlException ex)
            {
                WriteToLog("Failed to load Xml document");
                WriteToLog(ex.ToString());
                if (!NoErrorPrompts)
                {
                    Console.ReadLine();
                }
                Environment.Exit(-1);
            }
            try
            {
                //https://www.freeformatter.com/xpath-tester.html#ad-output
                //get some default settings
                NoPrompts              = bool.Parse(doc.SelectSingleNode("/DownloadInfo.xml/Settings/NoPrompts").InnerText.Trim());
                NoErrorPrompts         = bool.Parse(doc.SelectSingleNode("/DownloadInfo.xml/Settings/NoErrorPrompts").InnerText.Trim());
                ForceWriteFFBinaries   = bool.Parse(doc.SelectSingleNode("/DownloadInfo.xml/Settings/ForceWriteFFBinaries").InnerText.Trim());
                UpdateYoutubeDL        = bool.Parse(doc.SelectSingleNode("/DownloadInfo.xml/Settings/UpdateYoutubeDL").InnerText.Trim());
                ForceDownloadYoutubeDl = bool.Parse(doc.SelectSingleNode("/DownloadInfo.xml/Settings/ForceDownloadYoutubeDl").InnerText.Trim());
                CopyBinaries           = bool.Parse(doc.SelectSingleNode("/DownloadInfo.xml/Settings/CopyBinaries").InnerText.Trim());
                RunScripts             = bool.Parse(doc.SelectSingleNode("/DownloadInfo.xml/Settings/RunScripts").InnerText.Trim());
                SaveNewDate            = bool.Parse(doc.SelectSingleNode("/DownloadInfo.xml/Settings/SaveNewDate").InnerText.Trim());
                ParseTags              = bool.Parse(doc.SelectSingleNode("/DownloadInfo.xml/Settings/ParseTags").InnerText.Trim());
                CopyFiles              = bool.Parse(doc.SelectSingleNode("/DownloadInfo.xml/Settings/CopyFiles").InnerText.Trim());
                DeleteBinaries         = bool.Parse(doc.SelectSingleNode("/DownloadInfo.xml/Settings/DeleteBinaries").InnerText.Trim());
                DeleteOutputLogs       = bool.Parse(doc.SelectSingleNode("/DownloadInfo.xml/Settings/DeleteOutputLogs").InnerText.Trim());

                //and some default command line settings
                DefaultCommandLine             = doc.SelectSingleNode("/DownloadInfo.xml/CommandLine/Default").InnerText.Trim();
                DateAfterCommandLine           = doc.SelectSingleNode("/DownloadInfo.xml/CommandLine/DateAfter").InnerText.Trim();
                YoutubeMixDurationCommandLine  = doc.SelectSingleNode("/DownloadInfo.xml/CommandLine/YoutubeMixDuration").InnerText.Trim();
                YoutubeSongDurationCommandLine = doc.SelectSingleNode("/DownloadInfo.xml/CommandLine/YoutubeSongDuration").InnerText.Trim();
                YoutubeDlUrl       = UpdateTextFromXmlEntry(nameof(YoutubeDlUrl), YoutubeDlUrl, doc, "/DownloadInfo.xml/CommandLine/YoutubeDlUrl");
                CreateArchiveRegex = UpdateTextFromXmlEntry(nameof(CreateArchiveRegex), CreateArchiveRegex, doc, "/DownloadInfo.xml/CommandLine/CreateArchiveRegex");

                //for each xml element "DownloadInfo" in element "DownloadInfo.xml"
                int processingIndex = 0;
                foreach (XmlNode infosNode in doc.SelectNodes("//DownloadInfo.xml/DownloadInfos/DownloadInfo"))
                {
                    WriteToLog(string.Format("Processing DownloadInfo {0}", processingIndex));
                    DownloadInfo temp = new DownloadInfo();

                    //get list of fields in the DownloadInfo class
                    List <FieldInfo> fields = temp.GetType().GetFields().ToList();
                    foreach (XmlAttribute attribute in infosNode.Attributes)
                    {
                        //find a field with the matching attribute name
                        FieldInfo field = fields.Find(fieldd => fieldd.Name.Equals(attribute.Name));

                        //convert the string value to the data type
                        var converter = TypeDescriptor.GetConverter(field.FieldType);
                        try
                        {
                            field.SetValue(temp, converter.ConvertFrom(attribute.Value));
                        }
                        catch (Exception ex)
                        {
                            WriteToLog(string.Format("ERROR: Failed to parse xml attribute '{0}' to data type for DownloadInfo {1}", field.Name, temp.Folder));
                            WriteToLog(ex.ToString());
                            if (!NoErrorPrompts)
                            {
                                Console.ReadLine();
                            }
                            Environment.Exit(-1);
                        }
                    }

                    //parse DownloadUrl
                    temp.DownloadURL = infosNode[nameof(temp.DownloadURL)].InnerText.Trim();
                    if (string.IsNullOrWhiteSpace(temp.DownloadURL) && (temp.DownloadType == DownloadType.YoutubeMix || temp.DownloadType == DownloadType.YoutubeSong))
                    {
                        WriteToLog(string.Format("ERROR: DownloadURL for '{0}' is blank", temp.Folder));
                        if (!NoErrorPrompts)
                        {
                            Console.ReadLine();
                        }
                        Environment.Exit(-1);
                    }

                    //parse CustomYoutubeDlCommands
                    temp.CustomYoutubedlCommands = infosNode[nameof(temp.CustomYoutubedlCommands)].InnerText.Trim();

                    //parse CopyPaths
                    //get the list of paths that the parsed music files should be copied to
                    XmlNodeList pathsList = (infosNode as XmlElement).SelectNodes("CopyPath");
                    if (pathsList.Count > 0)
                    {
                        temp.CopyPaths = new string[pathsList.Count];
                        int i = 0;
                        foreach (XmlNode paths in pathsList)
                        {
                            //check to make sure the path is valid before trying to use later
                            if (!Directory.Exists(paths.InnerText))
                            {
                                if (temp.FirstRun)
                                {
                                    WriteToLog(string.Format("INFO: Path {0} does not exist, but firstRun = true, creating path", paths.InnerText));
                                    Directory.CreateDirectory(paths.InnerText);
                                }
                                else
                                {
                                    WriteToLog(string.Format("ERROR: The folder '{0}' declared in the xml does not exist", paths.InnerText));
                                    if (!NoErrorPrompts)
                                    {
                                        Console.ReadLine();
                                    }
                                    Environment.Exit(-1);
                                }
                            }

                            //check to make sure there's no duplicate CopyPath entries for this DownloadInfo
                            if (temp.CopyPaths.Contains(paths.InnerText))
                            {
                                WriteToLog("ERROR: Copy path already parsed, remove duplicate");
                                if (!NoErrorPrompts)
                                {
                                    Console.ReadLine();
                                }
                                Environment.Exit(-1);
                            }
                            temp.CopyPaths[i++] = paths.InnerText;
                        }
                    }
                    else
                    {
                        WriteToLog("ERROR: Paths count is 0 for downloadFolder of folder attribute " + temp.Folder);
                        if (!NoErrorPrompts)
                        {
                            Console.ReadLine();
                        }
                        Environment.Exit(-1);
                    }

                    //if it's the first time running, then we can set the last track count to 0 (if not already)
                    if (temp.FirstRun)
                    {
                        temp.LastTrackNumber = 0;
                    }

                    //and finally add it to the list
                    DownloadInfos.Add(temp);
                    processingIndex++;
                }
            }
            catch (Exception ex)
            {
                WriteToLog(ex.ToString());
                if (!NoErrorPrompts)
                {
                    Console.ReadLine();
                }
                Environment.Exit(-1);
            }
        }