/// <summary> /// Gets files to download from RSS feed /// </summary> /// <param name="xmlDoc">RSS feed</param> /// <returns>List of files to download</returns> public List <IliasFile> GetFilesFromXml(XmlDocument xmlDoc) { var rssFiles = new List <IliasFile>(); XmlNodeList items = xmlDoc.SelectNodes("//item"); string subfolderPattern = @"\[(.*?)\]"; string fileNamePattern = @"]\s(.*): Die Datei"; string fileIdPattern = @"file_(\d*)"; IliasFile[] filesArr = Files.ToArray(); foreach (XmlNode item in items) { if (item.SelectSingleNode(".//link") != null) { string link = item.SelectSingleNode(".//link").InnerText; if (link.Contains("target=file")) { string title = item.SelectSingleNode(".//title").InnerText; var subfoldersMatch = Regex.Match(title, subfolderPattern); string[] subfolders = subfoldersMatch.Groups[1].Value.Split(" > "); subfolders = subfolders.Select(sub => Util.ReplaceInvalidChars(sub)).ToArray(); string fileName = Util.ReplaceInvalidChars(Regex.Match(title, fileNamePattern).Groups[1].Value); int fileId = int.Parse(Regex.Match(link, fileIdPattern).Groups[1].Value); var fileUrl = new Uri($"https://ilias.uni-konstanz.de/ilias/goto_ilias_uni_file_{fileId}_download.html"); DateTime date = DateTime.Parse(item.SelectSingleNode(".//pubDate").InnerText); IliasFile fileToDownload = new IliasFile(subfolders, fileName, fileId, fileUrl, date); // Skip file if it exists on disk and either is ignored, extension is ignored, or file is not newer if (Array.Exists(ExistingFiles, f => f == fileName) && ( Array.Exists(Builder.Config.IgnoreFiles, f => f == fileName) || Array.Exists(Builder.Config.IgnoreExtensions, e => e == fileToDownload.Extension) || Array.Exists(filesArr, f => f.Equals(fileToDownload)) )) { continue; } rssFiles.Add(fileToDownload); } } } return(rssFiles); }
/// <summary> /// Downloads file from <c>fileUrl</c> to <c>path</c> /// </summary> /// <param name="path">Path to download directory</param> /// <param name="file">File to download</param> /// <param name="retrying">Retry download on failure</param> /// <returns>The task object representing integer 0 on success and -1 on failure</returns> public async Task <int> DownloadFile(string path, IliasFile file, bool retrying = true) { try { HttpResponseMessage response = await Client.GetAsync(file.Url); if (response.IsSuccessStatusCode) { DateTime lastModified = new DateTime(); if (response.Content.Headers.LastModified != null) { lastModified = DateTime.Parse(response.Content.Headers.LastModified.ToString()); } file.LastModified = lastModified; bool updated = false; if (Files.Count > 0) { IliasFile currentFile = Files.Find(f => f.Matches(file)); var idx = Files.FindIndex(f => f.Matches(file)); bool isOnDisk = Array.Exists(ExistingFiles, f => f == file.Name); if (currentFile != null) { if (isOnDisk) { if (currentFile.LastModified.CompareTo(file.LastModified) >= 0 && currentFile.Date.CompareTo(file.Date) >= 0) { return(0); } } updated = true; Files[idx] = file; } } Task <byte[]> fileArray = response.Content.ReadAsByteArrayAsync(); Directory.CreateDirectory(path); try { File.WriteAllBytes(Path.Combine(path, file.Name), await fileArray); if (!updated) { Files.Add(file); } Console.WriteLine($"{ILIAS_TAG} Downloaded { file.Name }"); return(0); } catch (IOException) { if (!retrying) { Console.WriteLine($"{ILIAS_TAG} An error occurred while writing { file.Name }. Please check manually if the file is correct."); return(-1); } Console.WriteLine($"{ILIAS_TAG} An error occurred while writing { file.Name }. Retrying."); return(await DownloadFile(path, file, false)); } } return(-1); } catch (TaskCanceledException e) { if (!retrying) { Console.WriteLine($"{ILIAS_TAG} Downloading { file.Name } failed."); Console.WriteLine(e.InnerException.Message); return(-1); } Console.WriteLine($"{ILIAS_TAG} An error occurred while downloading { file.Name }. Retrying."); Console.WriteLine(e); return(await DownloadFile(path, file, false)); } }
/// <summary> /// Gets URL to single exercises from <c>exercisePageUrl</c> /// </summary> /// <param name="exercisePageUrl">URL to page where the exercises are listed</param> /// <returns>The task object representing an array of lists where each element is an IliasFile of a single exercise file. /// The index 0 of the array contains exercise files and index 1 contains feedback files. Returns <c>null</c> on failure</returns> public async Task <List <IliasFile>[]> GetExerciseFileUrls(Uri exercisePageUrl) { try { var exerciseFiles = new List <IliasFile> [2]; exerciseFiles[0] = new List <IliasFile>(); exerciseFiles[1] = new List <IliasFile>(); HttpResponseMessage response = await Client.GetAsync(exercisePageUrl); HtmlDocument pageDocument = await Util.LoadHtmlDocument(response); HtmlNodeCollection rows = pageDocument.DocumentNode.SelectNodes("//div[@class='ilInfoScreenSec form-horizontal']"); HtmlNodeCollection navigation = pageDocument.DocumentNode.SelectSingleNode("//ol[@class='breadcrumb hidden-print']").SelectNodes(".//li"); var navigationSplit = new List <string>(); /* * TODO: Determine the exercise subfolder in a better way * The trigger word determines when to start adding the categories/subfolders from Ilias to the list. * This is used to determine which subfolder the exercise files belong to. * An example of navigation is: * Magazin, Mathematisch-Naturwissenschaftliche Sektion, Informatik und Informationswissenschaft, Lehrveranstaltungen WS 20/21, Data Visualization: Advanced Topics, Assignments, Assignment 01 */ string triggerWord = "Lehrveranstaltungen"; var foundTriggerWord = false; foreach (HtmlNode li in navigation) { string text = li.InnerText; if (foundTriggerWord) { navigationSplit.Add(text); } else if (text.StartsWith(triggerWord)) { foundTriggerWord = true; } } var tempSubfolders = navigationSplit.Select(sub => Util.ReplaceInvalidChars(sub)).ToArray(); foreach (HtmlNode row in rows) { HtmlNodeCollection links = row.SelectNodes(".//a"); if (links != null) { foreach (HtmlNode link in links) { HtmlAttribute hrefAttribute = link.Attributes["href"]; if (hrefAttribute != null) { string href = WebUtility.HtmlDecode(hrefAttribute.Value); if (href.Contains("file=")) { var url = new Uri(ILIAS_BASE_URL + href); var query = HttpUtility.ParseQueryString(url.Query); string fileName = query["file"]; int fileId = int.Parse(query["ref_id"]); var subfolderList = tempSubfolders.ToList(); if (href.Contains("cmd=downloadFile")) { var file = new IliasFile(tempSubfolders, fileName, fileId, url); exerciseFiles[0].Add(file); } else if (href.Contains("cmd=downloadFeedbackFile")) { var file = new IliasFile(tempSubfolders, "feedback_" + fileName, fileId, url); exerciseFiles[1].Add(file); } } } } } } return(exerciseFiles); } catch (TaskCanceledException e) { Console.WriteLine($"{ILIAS_TAG} Fetching exercise file URLs timed out."); Console.WriteLine(e.InnerException.Message); return(null); } }