/// <summary> /// Парсит и преобразовывает указанную строку, представляющую URI, /// и возвращает результат операции через выводной параметр /// </summary> /// <param name="Input"></param> /// <param name="ErrorMessage"></param> /// <returns></returns> public static Uri TryParseURI(String Input, out String ErrorMessage) { ErrorMessage = null; if (Input.HasAlphaNumericChars() == false) { ErrorMessage = "Input string has no any alphanumeric character"; return null; } Input = Input.CleanString().Trim(); if (Input.StartsWith("http://", StringComparison.OrdinalIgnoreCase) == false) { Input = "http://" + Input; } Uri input_URI; Boolean result = Uri.TryCreate(Input, UriKind.Absolute, out input_URI); if (result == false) { ErrorMessage = "Input string is invalid URI"; return null; } if (input_URI.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase)==false) { ErrorMessage = "Only HTTP scheme is valid, but not " + input_URI.Scheme; return null; } if (input_URI.Authority.Equals("myzuka.ru", StringComparison.OrdinalIgnoreCase)==false) { ErrorMessage = "Only myzuka.ru domain is supported, but not " + input_URI.Authority; return null; } if (input_URI.Segments.Length < 4) { ErrorMessage = "URI is invalid"; return null; } String segment1 = input_URI.Segments[1]; if (segment1.Trim('/').IsIn(StringComparison.OrdinalIgnoreCase, "album", "song") == false) { ErrorMessage = "First segment of URI - '"+segment1+"', is invalid"; return null; } String segment2 = input_URI.Segments[2]; if (segment2.Trim('/').TryParseNumber<UInt32>(NumberStyles.None, null)==null) { ErrorMessage = "Second segment of URI - '" + segment2 + "', is invalid"; return null; } return input_URI; }
/// <summary> /// Создаёт и возвращает новое задание по скачиванию указанных песен и последующему сохранению их на диск по указанному пути /// </summary> /// <param name="Songs">Список всех хидеров песен, файлы песен для которых следует скачать и вернуть. /// Не может быть NULL или пустым.</param> /// <param name="UserAgent">User-Agent, который будет использоваться при выполнении запросов</param> /// <param name="FolderPath">Существующий путь на диске, по которому будут сохранены песни. /// Если NULL, некорректный или не существует, будет выброшено исключение.</param> /// <param name="GenerateNewFilenames">Определяет, следует ли генерировать новое имя файла на основании тэгов песни (true), /// или же использовать то имя файла, которое "пришло" с сервера (false). Если будет указана генерация нового, однако /// получившееся имя будет некорректным, метод попытается его исправить. /// Если же исправить не получится, будет использовано имя с сервера.</param> /// <param name="FilenameTemplate">Шаблон имени файла песни</param> /// <returns></returns> public static ReactiveDownloader CreateTask (IList<OneSongHeader> Songs, String UserAgent, String FolderPath, Boolean GenerateNewFilenames, String FilenameTemplate) { if (UserAgent.HasAlphaNumericChars() == false) { throw new ArgumentException("User-Agent не может быть пустым", "UserAgent"); } if (FilePathTools.IsValidFilePath(FolderPath) == false) { throw new ArgumentException ("Путь для сохранения файлов песен = '" + FolderPath.ToStringS("NULL") + "' некорректен", "FolderPath"); } if(Songs == null) {throw new ArgumentNullException("Songs");} if(Songs.Any()==false) {throw new ArgumentException("Список песен для обработки не может быть пустым", "Songs");} if(GenerateNewFilenames == true && FilenameTemplate.HasAlphaNumericChars()==false) {throw new ArgumentException("Шаблон имени файла не может быть пуст, если требуется клиентская генерация имён файлов");} return new ReactiveDownloader(Songs, UserAgent, FolderPath, GenerateNewFilenames, FilenameTemplate); }
public Boolean HasAlphaNumericCharsTest(String input) { return input.HasAlphaNumericChars(); }
/// <summary> /// Возвращает текстовые данные, вычленяя их из части HTML-кода /// </summary> /// <param name="Input"></param> /// <returns></returns> private static String ExtractFromHTML(String Input) { if(Input.HasAlphaNumericChars()==false) {throw new ArgumentException("Входная строка не содержит цифробуквенных символов", "Input");} String temp = Input.MultiReplace(' ', new char[]{'\r', '\n', '\t'}).Trim(); temp = KlotosLib.HtmlTools.IntelliRemoveHTMLTags(temp); temp = StringTools.SubstringHelpers.ShrinkSpaces(temp).Trim(); temp = HttpUtility.HtmlDecode(temp); return temp; }
/// <summary> /// Пытается очистить от шелухи и возвратить URI /// </summary> /// <param name="Input"></param> /// <param name="ErrorMessage"></param> /// <returns></returns> internal static Uri TryFixReturnURI(String Input, out String ErrorMessage) { ErrorMessage = null; if (Input.HasAlphaNumericChars() == false) { ErrorMessage = "URI = '" + Input.ToStringS("NULL") + "' заведомо некорректен"; return null; } const String main_domain = "http://myzuka.ru"; const String download_domain = "http://fp"; String temp_URI = Input.Trim(new char[2] { ' ', '"' }); if (temp_URI.StartsWith(main_domain, StringComparison.OrdinalIgnoreCase) == false && temp_URI.StartsWith(download_domain, StringComparison.OrdinalIgnoreCase) == false) { temp_URI = main_domain + temp_URI; } temp_URI = HttpTools.DecodeUri(temp_URI); Uri link_URI; Boolean res = Uri.TryCreate(temp_URI, UriKind.Absolute, out link_URI); if (res == false) { ErrorMessage = String.Format( "Невозможно корректно распарсить как абсолютный URI подстроку '{0}', полученную из строки '{1}'", temp_URI, Input); return null; } return link_URI; }
internal static Dictionary<String, String> TransactionalApply(Boolean UseDistinctFolder, Boolean UseServerFilenames, String MaxDownloadThreads, String SavedFilesPath, String UserAgent, String FilenameTemplate) { Dictionary<String, String> output = new Dictionary<string, string>(); if (UserAgent.HasAlphaNumericChars() == false) { output.Add(UserAgent.MemberName(_ => UserAgent), "User-Agent can not be empty"); } if (SavedFilesPath.HasAlphaNumericChars() == false) { output.Add(SavedFilesPath.MemberName(_ => SavedFilesPath), "File path for saving files can not be empty"); } else if (FilePathTools.IsValidFilePath(SavedFilesPath) == false) { output.Add(SavedFilesPath.MemberName(_ => SavedFilesPath), "File path is invalid"); } Nullable<Byte> mdt = MaxDownloadThreads.TryParseNumber<Byte>(NumberStyles.None, CultureInfo.InvariantCulture); if (mdt == null) { output.Add(MaxDownloadThreads.MemberName(_ => MaxDownloadThreads), "Value '" + MaxDownloadThreads+"' is invalid"); } Char[] intersects = FilenameTemplate.ToCharArray().Intersect(Path.GetInvalidFileNameChars()).ToArray(); if (intersects.IsNullOrEmpty()==false) { output.Add(FilenameTemplate.MemberName(_ => FilenameTemplate)+"1", "Filename template contains next invalid characters: "+intersects.ConcatToString(", ")+"."); } if (FilenameTemplate.Contains(_TITLE_TOKEN, StringComparison.Ordinal) == false && FilenameTemplate.Contains(_NUMBER_TOKEN, StringComparison.Ordinal) == false) { output.Add(FilenameTemplate.MemberName(_ => FilenameTemplate)+"2", "Filename template must contain "+_TITLE_TOKEN+" or "+_NUMBER_TOKEN+" token"); } if (output.Any() == true) { return output; } Byte value = mdt.Value > (Byte)99 ? (Byte)99 : mdt.Value; ProgramSettings._instance = new ProgramSettings(UseDistinctFolder, UseServerFilenames, value, SavedFilesPath.Trim(), UserAgent.Trim(), FilenameTemplate.Trim()); return null; }
/// <summary> /// Декодирует URI, заменяя все escape-последовательности на небезопасные /// </summary> /// <param name="Input"></param> /// <returns></returns> public static String DecodeUri(String Input) { if (Input.HasAlphaNumericChars() == false) { return Input; } return HttpUtility.HtmlDecode(HttpUtility.UrlDecode(Input)); }
/// <summary> /// Возвращает все атрибуты вместе с их соответствующими значениями для указанного по имени тэга. /// Если тэгов несколько, будут возвращены атрибуты для первого встретившегося тэга. /// </summary> /// <param name="InputHTML">Входная HTML-содержащая строка. Не может быть NULL, пустой строкой или не содержать цифробуквенных символов.</param> /// <param name="TargetTagName">Имя целевого тэга, все атрибуты со значениями которого следует возвратить. /// Не может быть NULL, пустой строкой или не содержать цифробуквенных символов.</param> /// <param name="StartIndex">Начальная позиция входной HTML-содержащей строки, с которой следует начать поиск. Если 0 - поиск ведётся с начала. /// Если меньше 0 или больше длины исходной строки, выбрасывается исключение.</param> /// <returns>Список атрибутов вместе с их соответствующими значениями для указанного тэга в виде словаря, /// где ключ - атрибут, а его значение - значение атрибута. Если целевой тэг не найден, возвращает NULL. /// Если тэг найден, но он не содержит атрибутов, возвращается пустой словарь.</returns> public static Dictionary<String, String> GetAttributesForTag(String InputHTML, String TargetTagName, Int32 StartIndex) { if(InputHTML == null) {throw new ArgumentNullException("InputHTML");} if(InputHTML.HasAlphaNumericChars()==false) { throw new ArgumentException("Входная HTML-содержащая строка не содержит ни одной буквы или цифры и не является валидным HTML документом", "InputHTML"); } if (TargetTagName.HasAlphaNumericChars() == false) { throw new ArgumentException("Имя целевого тэга некорректно, так как не содержит ни одной буквы или цифры", "TargetTagName");} if (StartIndex < 0) { throw new ArgumentOutOfRangeException("StartIndex", StartIndex, "Начальная позиция не может быть меньше 0"); } if (StartIndex >= InputHTML.Length) { throw new ArgumentOutOfRangeException("StartIndex", StartIndex, String.Format("Начальная позиция ('{0}') не может быть больше или равна длине строки ('{1}')", StartIndex, InputHTML.Length)); } String cleared_tag_name = TargetTagName.Trim().ToLowerInvariant(); if (cleared_tag_name.StartsWith("<") == false) { cleared_tag_name = "<" + cleared_tag_name; } Int32 tag_start_pos = InputHTML.IndexOf(cleared_tag_name, StartIndex, StringComparison.OrdinalIgnoreCase); if (tag_start_pos == -1) { return null; } Int32 closing_bracket_pos = InputHTML.IndexOf(">", tag_start_pos + cleared_tag_name.Length, StringComparison.OrdinalIgnoreCase); if (InputHTML[closing_bracket_pos - 1] == '/') { closing_bracket_pos = closing_bracket_pos - 1; } string substring_with_attributes = InputHTML.SubstringWithEnd(tag_start_pos + cleared_tag_name.Length, closing_bracket_pos, false, false, false).Trim(); Dictionary<String, String> output = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase); if (substring_with_attributes.IsStringNullEmptyWhiteSpace()) { return output; } StringBuilder attribute_key_buffer = new StringBuilder(substring_with_attributes.Length); StringBuilder attribute_value_buffer = new StringBuilder(substring_with_attributes.Length); Boolean key_is_now = true; Boolean value_is_now = false; Boolean found_equal_sign = false; Boolean finished_pair = false; Boolean inside_quotes = false; Boolean value_without_quotes = false; Boolean whitespace_previous = false; foreach (Char one in substring_with_attributes) { if (Char.IsWhiteSpace(one)) { whitespace_previous = true; if (value_without_quotes == true) { value_without_quotes = false; value_is_now = false; key_is_now = false; } else if (value_is_now == true) { attribute_value_buffer.Append(one); } else { key_is_now = false; } continue; } if (one == '=') { whitespace_previous = false; if (inside_quotes && value_is_now) { attribute_value_buffer.Append(one); } else { key_is_now = false; value_is_now = false; found_equal_sign = true; } continue; } if (one == '"' || one == '\'') { whitespace_previous = false; inside_quotes = !inside_quotes; if (found_equal_sign) { key_is_now = false; value_is_now = true; } else { key_is_now = false; value_is_now = false; String attribute_key = attribute_key_buffer.ToString(); if (output.ContainsKey(attribute_key) == false) { output.Add(attribute_key, attribute_value_buffer.ToString()); } attribute_key_buffer.Clean(); attribute_value_buffer.Clean(); finished_pair = true; } found_equal_sign = false; continue; } if (value_is_now == false && found_equal_sign == false && inside_quotes == false && finished_pair == false && whitespace_previous == true) { String attribute_key = attribute_key_buffer.ToString(); if (output.ContainsKey(attribute_key) == false) { output.Add(attribute_key, attribute_value_buffer.ToString()); } attribute_key_buffer.Clean(); attribute_value_buffer.Clean(); finished_pair = true; } whitespace_previous = false; if (finished_pair == false && found_equal_sign == true && inside_quotes == false) { found_equal_sign = false; value_is_now = true; value_without_quotes = true; } if (value_is_now) { attribute_value_buffer.Append(one); continue; } if (finished_pair) { finished_pair = false; key_is_now = true; } if ((Char.IsLetterOrDigit(one) || one == '-' || one == ':') && key_is_now) { attribute_key_buffer.Append(one); continue; } } if (attribute_key_buffer.Length > 0) { String attribute_key = attribute_key_buffer.ToString(); if (output.ContainsKey(attribute_key) == false) { output.Add(attribute_key, attribute_value_buffer.ToString()); } } return output; }