Esempio n. 1
0
 /// <summary>
 /// Преобразование кирилицы в латиницу и обратно
 /// </summary>
 /// <param name="msg">текст для преобразования</param>
 /// <param name="obratno">метод перевода по умолчанию RU > EN</param>
 /// <returns></returns>
 public static string Translit(this string msg, bool obratno = false)
 {
     lock (_lock)
     {
         if (obratno)
         {
             return(Transliteration.Front(msg));
         }
         else
         {
             return(Transliteration.Back(msg));
         }
     }
 }
        public void TestTransliterateFromEnglishToRussian()
        {
            string engLetters = "abcdefghijklmnopqrstuvwxyz";

            // ISO
            Assert.Equal("абцдефгийклмнопрстувхыз", Transliteration.Back(engLetters));
            Assert.Equal("абвгдеёжзиклмнопрстуфхцчшщъыеюя", Transliteration.Back("abvgdeyozhziklmnoprstufxcchshshh'yeyuya"));
            Assert.Equal(Transliteration.Back(engLetters), Transliteration.Back(engLetters.ToUpper()).ToLower());

            // GOST
            Assert.Equal("абцдефгиклмнопрстувыз", Transliteration.Back(engLetters, Transliteration.TransliterationType.Gost));
            Assert.Equal("абвгдеёжзиклмнопрстуфхцчшщЪыеыуыа", Transliteration.Back("abvgdejozhziklmnoprstufkhcchshshh'yehyuya", Transliteration.TransliterationType.Gost));
            Assert.Equal(
                Transliteration.Back(engLetters, Transliteration.TransliterationType.Gost).ToLower(),
                Transliteration.Back(engLetters.ToUpper(), Transliteration.TransliterationType.Gost).ToLower());
        }
Esempio n. 3
0
        public XDocument Search(string searchPattern, string searchType = "", bool fb2Only = false, int pageNumber = 0, int threshold = 50)
        {
            if (!string.IsNullOrEmpty(searchPattern))
            {
                searchPattern = Uri.UnescapeDataString(searchPattern).Replace('+', ' ').ToLower();
            }

            XDocument doc = new XDocument(
                // Add root element and namespaces
                new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds),
                             new XElement("id", "tag:search:" + searchPattern),
                             new XElement("title", Localizer.Text("Search results")),
                             new XElement("updated", DateTime.UtcNow.ToUniversalTime()),
                             new XElement("icon", "/series.ico"),
                             // Add links
                             Links.opensearch, Links.search, Links.start, Links.self)
                );

            List <string> authors = new List <string>();
            List <Book>   titles  = new List <Book>();

            if (string.IsNullOrEmpty(searchType))
            {
                string transSearchPattern = Transliteration.Back(searchPattern, TransliterationType.GOST);
                authors = LibraryFactory.GetLibrary().GetAuthorsByName(searchPattern, true);
                if (authors.Count == 0 && !string.IsNullOrEmpty(transSearchPattern))
                {
                    authors = LibraryFactory.GetLibrary().GetAuthorsByName(transSearchPattern, true);
                }
                titles = LibraryFactory.GetLibrary().GetBooksByTitle(searchPattern);
                if (titles.Count == 0 && !string.IsNullOrEmpty(transSearchPattern))
                {
                    titles = LibraryFactory.GetLibrary().GetBooksByTitle(transSearchPattern);
                }
            }

            if (string.IsNullOrEmpty(searchType) && authors.Count > 0 && titles.Count > 0)
            {
                // Add two navigation entries: search by authors name and book title
                doc.Root.Add(
                    new XElement("entry",
                                 new XElement("updated", DateTime.UtcNow.ToUniversalTime()),
                                 new XElement("id", "tag:search:author"),
                                 new XElement("title", Localizer.Text("Search authors")),
                                 new XElement("content", Localizer.Text("Search authors by name"), new XAttribute("type", "text")),
                                 new XElement("link", new XAttribute("href", "/search?searchType=authors&searchTerm=" + Uri.EscapeDataString(searchPattern)), new XAttribute("type", "application/atom+xml;profile=opds-catalog"))),
                    new XElement("entry",
                                 new XElement("updated", DateTime.UtcNow.ToUniversalTime()),
                                 new XElement("id", "tag:search:title"),
                                 new XElement("title", Localizer.Text("Search books")),
                                 new XElement("content", Localizer.Text("Search books by title"), new XAttribute("type", "text")),
                                 new XElement("link", new XAttribute("href", "/search?searchType=books&searchTerm=" + Uri.EscapeDataString(searchPattern)), new XAttribute("type", "application/atom+xml;profile=opds-catalog")))
                    );
            }
            else if (searchType.Equals("authors") || (authors.Count > 0 && titles.Count == 0))
            {
                return(new AuthorsCatalog().GetCatalog(searchPattern, true));
            }
            else if (searchType.Equals("books") || (titles.Count > 0 && authors.Count == 0))
            {
                if (pageNumber > 0)
                {
                    searchPattern += "/" + pageNumber;
                }
                return(new BooksCatalog().GetCatalogByTitle(searchPattern, fb2Only, 0, 1000));
            }
            return(doc);
        }
Esempio n. 4
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="searchPattern"></param>
        /// <param name="threshold"></param>
        /// <returns></returns>
        public XDocument GetCatalog(string searchPattern, bool isOpenSearch = false, int threshold = 100)
        {
            if (!string.IsNullOrEmpty(searchPattern))
            {
                searchPattern = Uri.UnescapeDataString(searchPattern).Replace('+', ' ').ToLower();
            }

            XDocument doc = new XDocument(
                // Add root element and namespaces
                new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds),
                             new XElement("id", "tag:authors"),
                             new XElement("title", Localizer.Text("Books by authors")),
                             new XElement("updated", DateTime.UtcNow.ToUniversalTime()),
                             new XElement("icon", "/authors.ico"),
                             // Add links
                             Links.opensearch, Links.search, Links.start)
                );

            // Get all authors names starting with searchPattern
            List <string> Authors = Library.GetAuthorsByName(searchPattern, isOpenSearch);

            // For search, also check transliterated names
            if (isOpenSearch)
            {
                // Try transliteration
                string translit = Transliteration.Back(searchPattern, TransliterationType.GOST);
                if (!string.IsNullOrEmpty(translit))
                {
                    List <string> transAuthors = Library.GetAuthorsByName(translit, isOpenSearch);
                    if (transAuthors.Count > 0)
                    {
                        Authors.AddRange(transAuthors);
                    }
                }
            }

            // if there are more authors then threshold, try to collapse them into groups
            // and render these groups first and authors after them
            if (Authors.Count > threshold)
            {
                Dictionary <string, int> catalogGroups = null;
                do
                {
                    catalogGroups = (from a in Authors
                                     group a by(a.Length > searchPattern.Length ? a.Substring(0, searchPattern.Length + 1).Capitalize(true)
                                                                       : a.Capitalize(true)) into g
                                     where g.Count() > 1
                                     select new { Name = g, Count = g.Count() }).ToDictionary(x => x.Name.Key, y => y.Count);

                    if (catalogGroups.Count == 1)
                    {
                        searchPattern = catalogGroups.First().Key;
                    }
                    else
                    {
                        break;
                    }
                } while (true);

                // remove entry that exactly matches search pattern to avoid recursion
                catalogGroups.Remove(searchPattern.Capitalize(true));
                // remove entries that are groupped ( if any )
                foreach (var kv in catalogGroups)
                {
                    Authors.RemoveAll(a => a.StartsWith(kv.Key, StringComparison.InvariantCultureIgnoreCase));
                }

                // Add catalog groups
                foreach (KeyValuePair <string, int> cg in catalogGroups)
                {
                    doc.Root.Add(
                        new XElement("entry",
                                     new XElement("updated", DateTime.UtcNow.ToUniversalTime()),
                                     new XElement("id", "tag:authors:" + cg.Key),
                                     new XElement("title", cg.Key),
                                     new XElement("content", string.Format(Localizer.Text("Total authors on {0}: {1}"), cg.Key, cg.Value),
                                                  new XAttribute("type", "text")),
                                     new XElement("link", new XAttribute("href", "/authorsindex/" + Uri.EscapeDataString(cg.Key)),
                                                  new XAttribute("type", "application/atom+xml;profile=opds-catalog"))
                                     )
                        );
                }
            }

            // Add catalog entries
            foreach (string author in Authors)
            {
                var booksCount = Library.GetBooksByAuthorCount(author);

                doc.Root.Add(
                    new XElement("entry",
                                 new XElement("updated", DateTime.UtcNow.ToUniversalTime()),
                                 new XElement("id", "tag:authors:" + author),
                                 new XElement("title", author),
                                 new XElement("content", string.Format(Localizer.Text("Books: {0}"), booksCount), new XAttribute("type", "text")),
                                 new XElement("link", new XAttribute("href", "/author/" + Uri.EscapeDataString(author)), new XAttribute("type", "application/atom+xml;profile=opds-catalog"))
                                 )
                    );
            }
            return(doc);
        }
Esempio n. 5
0
        /// <summary>
        /// Returns books catalog for specific search
        /// </summary>
        /// <param name="searchPattern">Keyword to search</param>
        /// <param name="searchFor">Type of search</param>
        /// <param name="acceptFB2">Client can accept fb2 files</param>
        /// <param name="threshold">Items per page</param>
        /// <returns></returns>
        private XDocument GetCatalog(string searchPattern, SearchFor searchFor, bool acceptFB2, int threshold = 100)
        {
            if (!string.IsNullOrEmpty(searchPattern))
            {
                searchPattern = Uri.UnescapeDataString(searchPattern).Replace('+', ' ');
            }

            XDocument doc = new XDocument(
                // Add root element and namespaces
                new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds),
                             new XElement("id", "tag:books"),
                             new XElement("title", Localizer.Text("Books by author ") + searchPattern),
                             new XElement("updated", DateTime.UtcNow.ToUniversalTime()),
                             new XElement("icon", "/icons/books.ico"),
                             // Add links
                             Links.opensearch, Links.search, Links.start)
                );

            int pageNumber = 0;
            // Extract and remove page number from the search pattern
            int j = searchPattern.IndexOf('/');

            if (j > 0)
            {
                int.TryParse(searchPattern.Substring(j + 1), out pageNumber);
                searchPattern = searchPattern.Substring(0, j);
            }

            // Get author's books
            string      catalogType = string.Empty;
            List <Book> books       = new List <Book>();

            switch (searchFor)
            {
            case SearchFor.Author:
                books       = Library.GetBooksByAuthor(searchPattern);
                catalogType = "/author/" + Uri.EscapeDataString(searchPattern);
                break;

            case SearchFor.Sequence:
                books       = Library.GetBooksBySequence(searchPattern);
                catalogType = "/sequence/" + Uri.EscapeDataString(searchPattern);
                break;

            case SearchFor.Genre:
                books       = Library.GetBooksByGenre(searchPattern);
                catalogType = "/genre/" + Uri.EscapeDataString(searchPattern);
                break;

            case SearchFor.Title:
                books = Library.GetBooksByTitle(searchPattern);
                // For search, also return books by
                if (threshold > 50)
                {
                    string translit = Transliteration.Back(searchPattern, TransliterationType.GOST);
                    if (!string.IsNullOrEmpty(translit))
                    {
                        List <Book> transTitles = Library.GetBooksByTitle(translit);
                        if (transTitles.Count > 0)
                        {
                            books.AddRange(transTitles);
                        }
                    }
                }
                break;
            }

            // For sequences, sort books by sequence number
            if (searchFor == SearchFor.Sequence)
            {
                books = books.OrderBy(b => b.NumberInSequence).ToList();
            }
            // else sort by title
            else
            {
                books = books.OrderBy(b => b.Title, new OPDSComparer(TinyOPDS.Properties.Settings.Default.SortOrder > 0)).ToList();
            }

            int startIndex = pageNumber * threshold;
            int endIndex   = startIndex + ((books.Count / threshold == 0) ? books.Count : Math.Min(threshold, books.Count - startIndex));

            if (searchFor == SearchFor.Title)
            {
                if ((pageNumber + 1) * threshold < books.Count)
                {
                    catalogType = string.Format("/search?searchType=books&searchTerm={0}&pageNumber={1}", Uri.EscapeDataString(searchPattern), pageNumber + 1);
                    doc.Root.Add(new XElement("link",
                                              new XAttribute("href", catalogType),
                                              new XAttribute("rel", "next"),
                                              new XAttribute("type", "application/atom+xml;profile=opds-catalog")));
                }
            }
            else if ((pageNumber + 1) * threshold < books.Count)
            {
                catalogType += "/" + (pageNumber + 1);
                doc.Root.Add(new XElement("link",
                                          new XAttribute("href", catalogType),
                                          new XAttribute("rel", "next"),
                                          new XAttribute("type", "application/atom+xml;profile=opds-catalog")));
            }

            bool useCyrillic = TinyOPDS.Properties.Settings.Default.SortOrder > 0;

            List <Genre> genres = Library.Genres;

            // Add catalog entries
            for (int i = startIndex; i < endIndex; i++)
            {
                Book book = books.ElementAt(i);

                XElement entry =
                    new XElement("entry",
                                 new XElement("updated", DateTime.UtcNow.ToUniversalTime()),
                                 new XElement("id", "tag:book:" + book.ID),
                                 new XElement("title", book.Title)
                                 );

                foreach (string author in book.Authors)
                {
                    entry.Add(
                        new XElement("author",
                                     new XElement("name", author),
                                     new XElement("uri", "/author/" + Uri.EscapeDataString(author)
                                                  )));
                }

                foreach (string genreStr in book.Genres)
                {
                    Genre genre = genres.Where(g => g.Tag.Equals(genreStr)).FirstOrDefault();
                    if (genre != null)
                    {
                        entry.Add(new XElement("category", new XAttribute("term", (useCyrillic ? genre.Translation : genre.Name)), new XAttribute("label", (useCyrillic ? genre.Translation : genre.Name))));
                    }
                }

                // Build a content entry (translator(s), year, size, annotation etc.)
                string bookInfo = string.Empty;

                if (!string.IsNullOrEmpty(book.Annotation))
                {
                    bookInfo += string.Format(@"<p>{0}<br/></p>", System.Security.SecurityElement.Escape(book.Annotation.Trim()));
                }
                if (book.Translators.Count > 0)
                {
                    bookInfo += string.Format("<b>{0} </b>", Localizer.Text("Translation:"));
                    foreach (string translator in book.Translators)
                    {
                        bookInfo += translator + " ";
                    }
                    bookInfo += "<br/>";
                }
                if (book.BookDate != DateTime.MinValue)
                {
                    bookInfo += string.Format("<b>{0}</b> {1}<br/>", Localizer.Text("Year of publication:"), book.BookDate.Year);
                }

                if (!string.IsNullOrEmpty(book.Sequence))
                {
                    bookInfo += string.Format("<b>{0} {1} #{2}</b><br/>", Localizer.Text("Series:"), book.Sequence, book.NumberInSequence);
                }

                entry.Add(
                    new XElement(Namespaces.dc + "language", book.Language),
                    new XElement(Namespaces.dc + "format", book.BookType == BookType.FB2 ? "fb2+zip" : "epub+zip"),
                    new XElement("content", new XAttribute("type", "text/html"), XElement.Parse("<div>" + bookInfo + "<br/></div>")),
                    new XElement("format", book.BookType == BookType.EPUB ? "epub" : "fb2"),
                    new XElement("size", string.Format("{0} Kb", (int)book.DocumentSize / 1024)));


                if (book.HasCover)
                {
                    entry.Add(
                        // Adding cover page and thumbnail links
                        new XElement("link", new XAttribute("href", "/cover/" + book.ID + ".jpeg"), new XAttribute("rel", "http://opds-spec.org/image"), new XAttribute("type", "image/jpeg")),
                        new XElement("link", new XAttribute("href", "/cover/" + book.ID + ".jpeg"), new XAttribute("rel", "x-stanza-cover-image"), new XAttribute("type", "image/jpeg")),
                        new XElement("link", new XAttribute("href", "/thumbnail/" + book.ID + ".jpeg"), new XAttribute("rel", "http://opds-spec.org/thumbnail"), new XAttribute("type", "image/jpeg")),
                        new XElement("link", new XAttribute("href", "/thumbnail/" + book.ID + ".jpeg"), new XAttribute("rel", "x-stanza-cover-image-thumbnail"), new XAttribute("type", "image/jpeg"))
                        // Adding download links
                        );
                }

                string fileName = Uri.EscapeDataString(Transliteration.Front(string.Format("{0}_{1}", book.Authors.First(), book.Title)).SanitizeFileName());
                string url      = "/" + string.Format("{0}/{1}", book.ID, fileName);
                if (book.BookType == BookType.EPUB || (book.BookType == BookType.FB2 && !acceptFB2 && !string.IsNullOrEmpty(TinyOPDS.Properties.Settings.Default.ConvertorPath)))
                {
                    entry.Add(new XElement("link", new XAttribute("href", url + ".epub"), new XAttribute("rel", "http://opds-spec.org/acquisition/open-access"), new XAttribute("type", "application/epub+zip")));
                }

                if (book.BookType == BookType.FB2)
                {
                    entry.Add(new XElement("link", new XAttribute("href", url + ".fb2.zip"), new XAttribute("rel", "http://opds-spec.org/acquisition/open-access"), new XAttribute("type", "application/fb2+zip")));
                }

                // For search requests, lets add navigation links for author and series (if any)
                if (searchFor != SearchFor.Author)
                {
                    foreach (string author in book.Authors)
                    {
                        entry.Add(new XElement("link",
                                               new XAttribute("href", "/author/" + Uri.EscapeDataString(author)),
                                               new XAttribute("rel", "related"),
                                               new XAttribute("type", "application/atom+xml;profile=opds-catalog"),
                                               new XAttribute("title", string.Format(Localizer.Text("All books by author {0}"), author))));
                    }
                }

                if (searchFor != SearchFor.Sequence && !string.IsNullOrEmpty(book.Sequence))
                {
                    entry.Add(new XElement("link",
                                           new XAttribute("href", "/sequence/" + Uri.EscapeDataString(book.Sequence)),
                                           new XAttribute("rel", "related"),
                                           new XAttribute("type", "application/atom+xml;profile=opds-catalog"),
                                           new XAttribute("title", string.Format(Localizer.Text("All books by series {0}"), book.Sequence))));
                }

                doc.Root.Add(entry);
            }
            return(doc);
        }
Esempio n. 6
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="searchPattern"></param>
        /// <param name="threshold"></param>
        /// <returns></returns>
        public XDocument GetCatalog(string searchPattern, bool isOpenSearch = false, int threshold = 50)
        {
            if (!string.IsNullOrEmpty(searchPattern))
            {
                searchPattern = Uri.UnescapeDataString(searchPattern).Replace('+', ' ').ToLower();
            }

            XDocument doc = new XDocument(
                // Add root element and namespaces
                new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds),
                             new XElement("title", Localizer.Text("Books by authors")),
                             new XElement("updated", DateTime.UtcNow.ToUniversalTime()),
                             new XElement("icon", "/authors.ico"),
                             // Add links
                             Links.opensearch, Links.search, Links.start)
                );

            // Get all authors names starting with searchPattern
            List <string> Authors = LibraryFactory.GetLibrary().GetAuthorsByName(searchPattern, isOpenSearch);

            // For search, also check transliterated names
            if (isOpenSearch)
            {
                // Try transliteration
                string translit = Transliteration.Back(searchPattern, TransliterationType.GOST);
                if (!string.IsNullOrEmpty(translit))
                {
                    List <string> transAuthors = LibraryFactory.GetLibrary().GetAuthorsByName(translit, isOpenSearch);
                    if (transAuthors.Count > 0)
                    {
                        Authors.AddRange(transAuthors);
                    }
                }
            }

            if (Authors.Count > threshold)
            {
                Dictionary <string, int> authors = null;
                do
                {
                    authors = (from a in Authors
                               group a by(a.Length > searchPattern.Length ? a.Substring(0, searchPattern.Length + 1) : a) into g
                               where g.Count() > 1
                               select new { Name = g, Count = g.Count() }).ToDictionary(x => x.Name.Key, y => y.Count);
                    if (authors.Count == 1)
                    {
                        searchPattern = authors.First().Key;
                    }
                } while (authors.Count <= 1);

                // Add catalog entries
                foreach (KeyValuePair <string, int> author in authors)
                {
                    doc.Root.Add(
                        new XElement("entry",
                                     new XElement("updated", DateTime.UtcNow.ToUniversalTime()),
                                     new XElement("id", "tag:authors:" + author.Key),
                                     new XElement("title", author.Key),
                                     new XElement("content", string.Format(Localizer.Text("Total authors on {0}: {1}"), author.Key, author.Value), new XAttribute("type", "text")),
                                     new XElement("link", new XAttribute("href", "/authorsindex/" + Uri.EscapeDataString(author.Key)), new XAttribute("type", "application/atom+xml;profile=opds-catalog"))
                                     )
                        );
                }
            }
            //
            else
            {
                // Add catalog entries
                foreach (string author in Authors)
                {
                    var booksCount = LibraryFactory.GetLibrary().GetBooksByAuthorCount(author);

                    doc.Root.Add(
                        new XElement("entry",
                                     new XElement("updated", DateTime.UtcNow.ToUniversalTime()),
                                     new XElement("id", "tag:authors:" + author),
                                     new XElement("title", author),
                                     new XElement("content", string.Format(Localizer.Text("Books: {0}"), booksCount), new XAttribute("type", "text")),
                                     new XElement("link", new XAttribute("href", "/author/" + Uri.EscapeDataString(author)), new XAttribute("type", "application/atom+xml;profile=opds-catalog"))
                                     )
                        );
                }
            }
            return(doc);
        }