1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
4+ using System . Net . Http ;
5+ using System . Text . Json ;
6+ using System . Text . RegularExpressions ;
7+ using System . Threading . Tasks ;
8+ using Core . Configs ;
9+ using Core . Extensions ;
10+ using Core . Types . Book ;
11+ using Core . Types . Common ;
12+ using Core . Types . Librebook ;
13+ using HtmlAgilityPack ;
14+ using HtmlAgilityPack . CssSelectors . NetCore ;
15+ using Microsoft . Extensions . Logging ;
16+
17+ namespace Core . Logic . Getters ;
18+
19+ public class LibrebookGetter ( BookGetterConfig config ) : GetterBase ( config ) {
20+ protected override Uri SystemUrl => new ( "https://librebook.me/" ) ;
21+
22+ public override bool IsSameUrl ( Uri url ) {
23+ return SystemUrl . IsSameSubDomain ( SystemUrl ) ;
24+ }
25+
26+ protected override string GetId ( Uri url ) {
27+ return url . GetSegment ( 1 ) ;
28+ }
29+
30+ public override async Task Init ( ) {
31+ await base . Init ( ) ;
32+ Config . Client . DefaultRequestHeaders . Add ( "Referer" , SystemUrl . ToString ( ) ) ;
33+ }
34+
35+ public override async Task Authorize ( ) {
36+ if ( ! Config . HasCredentials ) {
37+ return ;
38+ }
39+
40+ var loginPage = "https://1.grouple.co/internal/auth/login?siteId=6&targetUri=%2Flogin%2FcontinueSso%3FsiteId%3D6%26targetUri%3D%252F" . AsUri ( ) ;
41+ var doc = await Config . Client . GetHtmlDocWithTriesAsync ( loginPage ) ;
42+
43+ var formUrl = loginPage . MakeRelativeUri ( doc . QuerySelector ( "form" ) ? . Attributes [ "action" ] ? . Value ) ;
44+ doc = await Config . Client . PostHtmlDocWithTriesAsync ( formUrl , GenerateAuthData ( ) ) ;
45+
46+ var noty = Regex . Match ( doc . ParsedText , @"showNoty\((.*?)\)" ) ;
47+ var result = JsonSerializer . Deserialize < LibrebookAuthResponse > ( noty . Groups [ 1 ] . Value ) ;
48+ if ( result . Type == "success" ) {
49+ Config . Logger . LogInformation ( "Успешно авторизовались" ) ;
50+ } else {
51+ throw new Exception ( $ "Не удалось авторизоваться. { result . Text } ") ;
52+ }
53+ }
54+
55+ private FormUrlEncodedContent GenerateAuthData ( ) {
56+ var payload = new Dictionary < string , string > {
57+ { "targetUri" , "/login/continueSso?siteId=6&targetUri=%2F" } ,
58+ { "username" , Config . Options . Login } ,
59+ { "password" , Config . Options . Password } ,
60+ { "remember_me" , "true" } ,
61+ { "_remember_me_yes" , string . Empty } ,
62+ { "remember_me_yes" , "on" } ,
63+ } ;
64+
65+ return new FormUrlEncodedContent ( payload ) ;
66+ }
67+
68+ public override async Task < Book > Get ( Uri url ) {
69+ url = SystemUrl . MakeRelativeUri ( GetId ( url ) ) . ReplaceHost ( url . Host ) ;
70+ var doc = await Config . Client . GetHtmlDocWithTriesAsync ( url ) ;
71+
72+ var chapterLink = doc . QuerySelector ( "a.chapter-link" ) ;
73+ if ( chapterLink == default ) {
74+ throw new Exception ( "Книга недоступна для чтения. Возможно, нужна авторизация." ) ;
75+ }
76+
77+ var startUrl = url . MakeRelativeUri ( chapterLink . Attributes [ "href" ] . Value ) ;
78+ var book = new Book ( url ) {
79+ Cover = await GetCover ( doc , url ) ,
80+ Chapters = await FillChapters ( startUrl ) ,
81+ Title = doc . GetTextBySelector ( "h1.names > span.name" ) ,
82+ Author = GetAuthor ( doc , url ) ,
83+ Annotation = doc . QuerySelector ( "div.manga-description > div" ) ? . InnerHtml
84+ } ;
85+
86+ return book ;
87+ }
88+
89+ private Author GetAuthor ( HtmlDocument doc , Uri bookUrl ) {
90+ var a = doc . QuerySelector ( "span.elem_author a" ) ;
91+ return a == default ? new Author ( "Librebook" ) : new Author ( a . GetText ( ) , bookUrl . MakeRelativeUri ( a . Attributes [ "href" ] . Value ) ) ;
92+ }
93+
94+ private async Task < IEnumerable < Chapter > > FillChapters ( Uri startUrl ) {
95+ var result = new List < Chapter > ( ) ;
96+ if ( Config . Options . NoChapters ) {
97+ return result ;
98+ }
99+
100+ foreach ( var urlChapter in await GetToc ( startUrl ) ) {
101+ Config . Logger . LogInformation ( $ "Загружаю главу { urlChapter . Title . CoverQuotes ( ) } ") ;
102+ var chapter = new Chapter {
103+ Title = urlChapter . Title
104+ } ;
105+
106+ var chapterDoc = await GetChapter ( urlChapter ) ;
107+
108+ if ( chapterDoc != default ) {
109+ chapter . Images = await GetImages ( chapterDoc , SystemUrl ) ;
110+ chapter . Content = chapterDoc . DocumentNode . InnerHtml ;
111+ }
112+
113+ result . Add ( chapter ) ;
114+ }
115+
116+ return result ;
117+ }
118+
119+ private async Task < IEnumerable < UrlChapter > > GetToc ( Uri startUrl ) {
120+ var doc = await Config . Client . GetHtmlDocWithTriesAsync ( startUrl ) ;
121+ var result = doc
122+ . QuerySelectorAll ( "#chapters-list a.chapter-link" )
123+ . Select ( a => new UrlChapter ( SystemUrl . MakeRelativeUri ( a . Attributes [ "href" ] . Value ) , a . GetText ( ) . ReplaceNewLine ( ) ) )
124+ . ToList ( ) ;
125+
126+ return SliceToc ( result , c => c . Title ) ;
127+ }
128+
129+ private async Task < HtmlDocument > GetChapter ( UrlChapter urlChapter ) {
130+ var doc = await Config . Client . GetHtmlDocWithTriesAsync ( urlChapter . Url ) ;
131+ return doc . QuerySelector ( "div.b-chapter" ) . InnerHtml . AsHtmlDoc ( ) ;
132+ }
133+
134+ private Task < TempFile > GetCover ( HtmlDocument doc , Uri bookUri ) {
135+ var imagePath = doc . QuerySelector ( "img.fotorama__img" ) ? . Attributes [ "src" ] ? . Value ;
136+ return ! string . IsNullOrWhiteSpace ( imagePath ) ? SaveImage ( bookUri . MakeRelativeUri ( imagePath ) ) : Task . FromResult ( default ( TempFile ) ) ;
137+ }
138+ }
0 commit comments