Compare commits

...
28 Commits
Author SHA1 Message Date
Mrk 7a6328f3d9 Vrs 0.5.3.2
Compilata da CSiCoAlm il 2026/01/05
- Update formattazione testi per eliminare problemi con accenti e caratteri html
- Update formattazione tags per uniformare quelli di A69 e di Old_Milu

ToDo:
- Aggiungere gestione parallela della FIFO secondo vecchi modello per aumentare il feedback alla UI
- Inserire anche l'estrazione dei racconti
- Inserire funzionalità avanzate sul lettore

Merge pull request 'Migliorata formattazione dei testi e dei tags' (#8) from mrk/Upgrade format into master
Reviewed-on: http://mrkalm.ddns.net:34333/MrkPi/A69WD/pulls/8
Command:  git push origin HEAD:refs/for/master -o topic="Upgrade format" -o title="Migliorata formattazione dei testi e dei tags"
2026-01-05 16:26:34 +01:00
CSiCoAlm c9075fabc8 Upgradato la formattazione dei testi e dei tags 2026-01-05 16:23:04 +01:00
Mrk 9f32778d0a Vrs 0.5.3.1
Compilata da CSiCoAlm il 2026/01/04
- Inserito serializzazione su XML dei racconti
- Inserito importazione dei file del vecchio sito Milu che sono completamente non compatibili con i formati attuali.

ToDo:
- Aggiungere gestione parallela della FIFO secondo vecchi modello per aumentare il feedback alla UI
- Inserire anche l'estrazione dei racconti
- Inserire funzionalità avanzate sul lettore

Merge pull request 'Serializzazione dei racconti in XML e importazione sito Old Milu' (#7) from mrk/Export XML into master
Reviewed-on: http://mrkalm.ddns.net:34333/MrkPi/A69WD/pulls/7
Command:  git push origin HEAD:refs/for/master -o topic="Export XML" -o title="Serializzazione dei racconti in XML e importazione sito Old Milu"
2026-01-04 18:55:14 +01:00
CSiCoAlm 04a27b9b19 Inserito importazione dei file del vecchio sito Milu 2026-01-04 18:52:06 +01:00
CSiCoAlm 09606b60ae Inserito serializzazione su XML dei racconti 2026-01-04 11:38:20 +01:00
Mrk 6b7d77d1a4 Vrs 0.5.3.0
Compilata da CSiCoAlm il 2026/01/01
Inserito nello skraping anche la sorgente eRac

ToDo:
- Aggiungere gestione parallela della FIFO secondo vecchi modello per aumentare il feedback alla UI
- Inserire anche l'estrazione dei racconti
- Inserire funzionalità avanzate sul lettore

Merge pull request 'Gestire anche la sorgente eRac nello skraping' (#6) from mrk/Skraping eRac into master
Reviewed-on: http://mrkalm.ddns.net:34333/MrkPi/A69WD/pulls/6
Comand:  git push origin HEAD:refs/for/master -o topic="Skraping eRac" -o title="Gestire anche la sorgente eRac nello skraping"
2026-01-02 17:57:41 +01:00
CSiCoAlm f8685ad65d Testato nuovo skraping 2026-01-02 17:53:38 +01:00
CSiCoAlm 89ca167bcc Inserito anche lo skraping di eRac 2026-01-02 17:48:06 +01:00
Mrk ec65be49d8 Vrs 0.5.2.0
Compilata da CSiCoAlm il 2026/01/01
Refactoring del downloader per poterlo utilizzare in ATDB:
- imglobata la classe iSite nella iLink
- impostato cartelle di destinazione dei file per distinguere la diagnostica del lavoro

ToDo:
- Aggiungere gestione parallela della FIFO secondo vecchi modello per aumentare il feedback alla UI
- Inserire anche l'estrazione dei racconti
- Inserire funzionalità avanzate sul lettore

Merge pull request 'Migliorato lo skraping' (#5) from mrk/Skraping Upgrade into master
Reviewed-on: http://mrkalm.ddns.net:34333/MrkPi/A69WD/pulls/5
Comand:  git push origin HEAD:refs/for/master -o topic="Skraping Upgrade" -o title="Migliorato lo skraping"
2026-01-01 16:14:21 +01:00
CSiCoAlm d09e9cbf39 Update della UI dopo l'upgrade dello skraper e dell'importer 2026-01-01 16:12:28 +01:00
CSiCoAlm 0d2b5a1f49 Upgrade dello skraping
Refactoring (eliminata la iSite) e cartelle di destinazione distinte in base alla diagnostica.
2026-01-01 15:51:38 +01:00
CSiCoAlm cc828ff4ee Upgradato lo skraping impostando salvataggi diversi per ciasucn file. 2026-01-01 15:10:37 +01:00
Mrk 1c372a3632 Vrs 0.5.1.0
Compilata da CSiCoAlm il 2026/01/01
Refactoring del downloader per poterlo utilizzare in ATDB
Upgradato l'importazione, selezionando casualmente i file da importare,
gestendo il numero massimo di file e la cartella da importare, sul file di configurazione.

ToDo:
- Aggiungere gestione parallela della FIFO secondo vecchi modello per aumentare il feedback alla UI
- Inserire anche l'estrazione dei racconti
- Inserire funzionalità avanzate sul lettore

Merge pull request 'Migliorata l'importazione' (#4) from mrk/Import Upgrade into master
Reviewed-on: http://mrkalm.ddns.net:34333/MrkPi/A69WD/pulls/4
Command: git push origin HEAD:refs/for/master -o topic="Import Upgrade" -o title="Migliorata l'importazione"
2026-01-01 14:05:02 +01:00
CSiCoAlm 63e769810f Refactoring e migliorata importazione 2026-01-01 14:02:44 +01:00
CSiCoAlm dfe56ef6ae Test download con più client 2026-01-01 12:23:58 +01:00
Mrk 0ae8fee6a1 Vrs 0.5.0.0
Compilata da CSiCoAlm il 2025/12/31
Inserito reader basilare (solo lettura, navigazione random o sequenziale)
Integrato import e skraping sul reader

ToDo:
- Aggiungere gestione parallela della FIFO secondo vecchi modello per aumentare il feedback alla UI
- Inserire anche l'estrazione dei racconti
- Inserire funzionalità avanzate sul lettore

Merge pull request 'Nuova versione con reader integrato' (#3) from mrk/Versione Reader into master
Reviewed-on: http://mrkalm.ddns.net:34333/MrkPi/A69WD/pulls/3
Command: git push origin HEAD:refs/for/master -o topic="Versione Reader" -o title="Nuova versione con reader integrato"
2025-12-31 18:26:17 +01:00
CSiCoAlm 0ba38dab69 Inserita voce menu per visualizzare il log 2025-12-31 18:23:48 +01:00
CSiCoAlm b88ba4b8a6 Aumentato la diagnostica nella sequenza di autenticazione al sito 2025-12-31 18:07:31 +01:00
Mrk ab758b77d9 Merge pull request 'Inserire un lettore per i racconti' (#8) from mrk/Insert Reader into master
Reviewed-on: http://mrkalm.ddns.net:34333/MrkPi/A69WD/pulls/8
2025-12-29 14:30:26 +01:00
CSiCoAlm 44b9c095cd Integrato l'import nello skraper
Modificato lo skraper per includere anche l'import del racconto
Testato velocemente

ToDo:
- [X] Inserire un lettore per visualizzare i racconti nel DB
- [0/3] Inserire funzionalità avanzate di lettura:
  - [ ] navigazione avanzata fra i racconti (per id, per autore, per titolo, per tag ecc...
  - [ ] possibilità di filtrare i racconti
  - [ ] Notificon per poter nascondere il reader e centralizzare i comandi
- [3/3] Integrare lo scraping dei racconti dal web e l'import da file
  - [X] Integrare lo scraping dei racconti da web, visualizzandolo sulla progress bar e label nella status bar.
  - [X] Integrare l'import dei racconti da file, visualizzandolo sulla progress bar e label nella status bar.
  - [X] Upgradare lo skraping, importando i racconti via via che sono downloadati
2025-12-29 14:27:26 +01:00
CSiCoAlm 00ad69e4e8 Integrato l'import su thread separato
Integrato menu contestuale per comandare il thread secondario di import
Aggiornata UI per visualizzare eventi del thread separato
Testato import

ToDo:
- [X] Inserire un lettore per visualizzare i racconti nel DB
- [0/3] Inserire funzionalità avanzate di lettura:
  - [ ] navigazione avanzata fra i racconti (per id, per autore, per titolo, per tag ecc...
  - [ ] possibilità di filtrare i racconti
  - [ ] Notificon per poter nascondere il reader e centralizzare i comandi
- [2/3] Integrare lo scraping dei racconti dal web e l'import da file
  - [X] Integrare lo scraping dei racconti da web, visualizzandolo sulla progress bar e label nella status bar.
  - [X] Integrare l'import dei racconti da file, visualizzandolo sulla progress bar e label nella status bar.
  - [ ] Upgradare lo skraping, importando i racconti via via che sono downloadati
2025-12-29 13:38:37 +01:00
CSiCoAlm 8233e4edf1 Integrato lo scraping su thread separato
Integrato menu contestuale per comandare il thread secondario di skraping
Aggiornata UI per visualizzare eventi del thread separato
Testato scraping

ToDo:
- [X] Inserire un lettore per visualizzare i racconti nel DB
- [0/3] Inserire funzionalità avanzate di lettura:
  - [ ] navigazione avanzata fra i racconti (per id, per autore, per titolo, per tag ecc...
  - [ ] possibilità di filtrare i racconti
  - [ ] Notificon per poter nascondere il reader e centralizzare i comandi
- [0/3] Integrare lo scraping dei racconti dal web e l'import da file
  - [X] Integrare lo scraping dei racconti da web, visualizzandolo sulla progress bar e label nella status bar.
  - [ ] Integrare l'import dei racconti da file, visualizzandolo sulla progress bar e label nella status bar.
  - [ ] Upgradare lo skraping, importando i racconti via via che sono downloadati
2025-12-29 13:29:06 +01:00
CSiCoAlm 1fd91b4af2 Inserito Reader
Esempio base di lettore, con solo funzionalità di lettura random o sequenziale (frecce su/giu).
Testato e la lettura è corretta.

ToDo:
- [X] Inserire un lettore per visualizzare i racconti nel DB
- [ ] Inserire funzionalità avanzate di lettura (navigazione, filtri, ecc...)
- [0/2] Integrare lo scraping dei racconti dal web
   - [ ] Integrare lo scraping visualizzandolo sulla progress bar e label nella status bar.
   - [ ] Importare i racconti via via che sono downloadati
2025-12-29 11:48:05 +01:00
Mrk b43dcbbb88 Integrato spunti ottenuti da una chat con Gemini
Test degli spunti ottenuti da una chat con Gemini.
- [X] Test della classe cRac.cs
- [X] Test della funzione di salvataggio sul DB
- [X] Test della funzione di lettura dal DB
- [ ] Test della funzione di ricerca nel DB
- [ ] Test funzione di cancellazione nel DB
- [ ] Test nella ricerca di duplicati
- [X] Test della funzione di esportazione in html con stile
- [ ] Inserire verifica su racconto duplicato, prima di salvare un nuovo racconto.

---
Merge pull request 'Testato spunti ottenuti da una chat con Gemini' (#7) from mrk/Issue: MrkPi/A69WD#6 into master
Reviewed-on: http://mrkalm.ddns.net:34333/MrkPi/A69WD/pulls/7
Cmd: git push origin HEAD:refs/for/master -o topic="Issue: MrkPi/A69WD#6" -o title="Testato spunti ottenuti da una chat con Gemini"
2025-12-29 11:33:06 +01:00
CSiCoAlm 0db12c844f Test funzione di salvataggio sul DB e refactoring del codice
Test degli spunti ottenuti da una chat con Gemini.
- [X] Test della classe cRac.cs
- [X] Test della funzione di salvataggio sul DB
- [X] Test della funzione di lettura dal DB
- [ ] Test della funzione di ricerca nel DB
- [ ] Test funzione di cancellazione nel DB
- [ ] Test nella ricerca di duplicati
- [X] Test della funzione di esportazione in html con stile
- [ ] Inserire verifica su racconto duplicato, prima di salvare un nuovo racconto.

Riepilogo della chat:
- connesso a gemini con chrome e utente mirko
- dato in pasto il file *ATDB Database Schema.sql* con la struttura del DB
- richiesto la classe cRac per rappresentare un racconto.
- ottenuto *cRac.cs*
- richiesto come salvare un cRac sul DB con diagnostica errori ed evitando spurie sul DB
- ottenuto *Salvataggio cRac su DB.cs*
- richiesto come leggere un racconto
- ottenuto *GetRaccontoById.cs*
- richiesto: ragruppiamo le funzioni di helper su un file Helper.cs e quelle di lettura/scrittura sul DB sul file DB.cs
- ottenuto *Helper.cs* e *DB.cs*
- richiesto: facciamo le ricerche avanzate per titolo, tag, autore, data di creazione, sorgente
- ottenuta la nuova versione di *Aggiornamento 1 di DB.cs*
- richiesto come eliminare un racconto dal DB
- ottenuta la nuova versione di *Aggiornamento 2 di DB.cs*
- richiesto: come ricercare racconti duplicati
- ottenuto spunti di ricerca salvati in *Ricerca duplicati.cs*
- richiesto come fare il merge di racconti duplicati
- ottenuto *merge.cs*
- richiesto delle idee per la UI
- ottenuto il file *UI_idee.md*
- richiesto un esempio di esportazione di un racconto in html con stile
- ottenuto: *export.cs*
2025-12-28 17:10:00 +01:00
CSiCoAlm 56920d059d Test funzione di lettura dal DB: GetRacById(IDrac)
Test degli spunti ottenuti da una chat con Gemini.
- [X] Test della classe cRac.cs
- [ ] Test della funzione di salvataggio sul DB
- [X] Test della funzione di lettura dal DB
- [ ] Test della funzione di ricerca nel DB
- [ ] Test funzione di cancellazione nel DB
- [ ] Test nella ricerca di duplicati
- [X] Test della funzione di esportazione in html con stile

Riepilogo della chat:
- connesso a gemini con chrome e utente mirko
- dato in pasto il file *ATDB Database Schema.sql* con la struttura del DB
- richiesto la classe cRac per rappresentare un racconto.
- ottenuto *cRac.cs*
- richiesto come salvare un cRac sul DB con diagnostica errori ed evitando spurie sul DB
- ottenuto *Salvataggio cRac su DB.cs*
- richiesto come leggere un racconto
- ottenuto *GetRaccontoById.cs*
- richiesto: ragruppiamo le funzioni di helper su un file Helper.cs e quelle di lettura/scrittura sul DB sul file DB.cs
- ottenuto *Helper.cs* e *DB.cs*
- richiesto: facciamo le ricerche avanzate per titolo, tag, autore, data di creazione, sorgente
- ottenuta la nuova versione di *Aggiornamento 1 di DB.cs*
- richiesto come eliminare un racconto dal DB
- ottenuta la nuova versione di *Aggiornamento 2 di DB.cs*
- richiesto: come ricercare racconti duplicati
- ottenuto spunti di ricerca salvati in *Ricerca duplicati.cs*
- richiesto come fare il merge di racconti duplicati
- ottenuto *merge.cs*
- richiesto delle idee per la UI
- ottenuto il file *UI_idee.md*
- richiesto un esempio di esportazione di un racconto in html con stile
- ottenuto: *export.cs*
2025-12-28 15:50:44 +01:00
CSiCoAlm 8fbe7866bf Testato classe cRac.cs
Test degli spunti ottenuti da una chat con Gemini.
- [X] Test della classe cRac.cs
- [ ] Test della funzione di salvataggio sul DB
- [ ] Test della funzione di lettura dal DB
- [ ] Test della funzione di ricerca nel DB
- [ ] Test funzione di cancellazione nel DB
- [ ] Test nella ricerca di duplicati
- [X] Test della funzione di esportazione in html con stile

Riepilogo della chat:
- connesso a gemini con chrome e utente mirko
- dato in pasto il file *ATDB Database Schema.sql* con la struttura del DB
- richiesto la classe cRac per rappresentare un racconto.
- ottenuto *cRac.cs*
- richiesto come salvare un cRac sul DB con diagnostica errori ed evitando spurie sul DB
- ottenuto *Salvataggio cRac su DB.cs*
- richiesto come leggere un racconto
- ottenuto *GetRaccontoById.cs*
- richiesto: ragruppiamo le funzioni di helper su un file Helper.cs e quelle di lettura/scrittura sul DB sul file DB.cs
- ottenuto *Helper.cs* e *DB.cs*
- richiesto: facciamo le ricerche avanzate per titolo, tag, autore, data di creazione, sorgente
- ottenuta la nuova versione di *Aggiornamento 1 di DB.cs*
- richiesto come eliminare un racconto dal DB
- ottenuta la nuova versione di *Aggiornamento 2 di DB.cs*
- richiesto: come ricercare racconti duplicati
- ottenuto spunti di ricerca salvati in *Ricerca duplicati.cs*
- richiesto come fare il merge di racconti duplicati
- ottenuto *merge.cs*
- richiesto delle idee per la UI
- ottenuto il file *UI_idee.md*
- richiesto un esempio di esportazione di un racconto in html con stile
- ottenuto: *export.cs*
2025-12-28 14:32:57 +01:00
CSiCoAlm 1cc4528295 Testato esportazione in html
Test degli spunti ottenuti da una chat con Gemini.
- [ ] Test della classe cRac.cs
- [ ] Test della funzione di salvataggio sul DB
- [ ] Test della funzione di lettura dal DB
- [ ] Test della funzione di ricerca nel DB
- [ ] Test funzione di cancellazione nel DB
- [ ] Test nella ricerca di duplicati
- [X] Test della funzione di esportazione in html con stile

Riepilogo della chat:
- connesso a gemini con chrome e utente mirko
- dato in pasto il file *ATDB Database Schema.sql* con la struttura del DB
- richiesto la classe cRac per rappresentare un racconto.
- ottenuto *cRac.cs*
- richiesto come salvare un cRac sul DB con diagnostica errori ed evitando spurie sul DB
- ottenuto *Salvataggio cRac su DB.cs*
- richiesto come leggere un racconto
- ottenuto *GetRaccontoById.cs*
- richiesto: ragruppiamo le funzioni di helper su un file Helper.cs e quelle di lettura/scrittura sul DB sul file DB.cs
- ottenuto *Helper.cs* e *DB.cs*
- richiesto: facciamo le ricerche avanzate per titolo, tag, autore, data di creazione, sorgente
- ottenuta la nuova versione di *Aggiornamento 1 di DB.cs*
- richiesto come eliminare un racconto dal DB
- ottenuta la nuova versione di *Aggiornamento 2 di DB.cs*
- richiesto: come ricercare racconti duplicati
- ottenuto spunti di ricerca salvati in *Ricerca duplicati.cs*
- richiesto come fare il merge di racconti duplicati
- ottenuto *merge.cs*
- richiesto delle idee per la UI
- ottenuto il file *UI_idee.md*
- richiesto un esempio di esportazione di un racconto in html con stile
- ottenuto: *export.cs*
2025-12-28 13:52:05 +01:00
25 changed files with 3826 additions and 1183 deletions

No files matched your search

+1 -1
View File
@@ -1 +1 @@
{"RootPath":"C:\\Commesse\\_zPrivate\\A69WD\\Src\\A69WD","ProjectFileName":"A69WD.csproj","Configuration":"Debug|AnyCPU","FrameworkPath":"","Sources":[{"SourceFile":"App.cs"},{"SourceFile":"Data\\cNota.cs"},{"SourceFile":"Data\\cRac.cs"},{"SourceFile":"Data\\DBTools.cs"},{"SourceFile":"Net\\eLinkState.cs"},{"SourceFile":"Net\\cLink_A69.cs"},{"SourceFile":"Service\\Service_Crawler.cs"},{"SourceFile":"Site\\cSite_A69.cs"},{"SourceFile":"UI\\frmDownloader.cs"},{"SourceFile":"UI\\frmDownloader.Designer.cs"},{"SourceFile":"General\\Cfg.cs"},{"SourceFile":"General\\cTools.cs"},{"SourceFile":"General\\Logger.cs"},{"SourceFile":"Net\\iLink.cs"},{"SourceFile":"Properties\\AssemblyInfo.cs"},{"SourceFile":"Site\\iSiteType.cs"},{"SourceFile":"Properties\\Resources.Designer.cs"},{"SourceFile":"Properties\\Settings.Designer.cs"},{"SourceFile":"obj\\Debug\\.NETFramework,Version=v4.8.AssemblyAttributes.cs"}],"References":[{"Reference":"C:\\Commesse\\_zPrivate\\A69WD\\Src\\_Lib\\HtmlAgilityPack.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\Microsoft.CSharp.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\mscorlib.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Core.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Data.DataSetExtensions.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Data.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Commesse\\_zPrivate\\A69WD\\Src\\_Lib\\System.Data.SQLite.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Deployment.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Drawing.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Net.Http.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Windows.Forms.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Xml.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Xml.Linq.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""}],"Analyzers":[],"Outputs":[{"OutputItemFullPath":"C:\\Commesse\\_zPrivate\\A69WD\\Src\\A69WD\\bin\\Debug\\A69WD.exe","OutputItemRelativePath":"A69WD.exe"},{"OutputItemFullPath":"","OutputItemRelativePath":""}],"CopyToOutputEntries":[]}
{"RootPath":"C:\\Commesse\\_zPrivate\\A69WD\\Src\\A69WD","ProjectFileName":"A69WD.csproj","Configuration":"Debug|AnyCPU","FrameworkPath":"","Sources":[{"SourceFile":"App.cs"},{"SourceFile":"Data\\cNota.cs"},{"SourceFile":"Data\\cRac.cs"},{"SourceFile":"Data\\DBTools.cs"},{"SourceFile":"Data\\Helper.cs"},{"SourceFile":"General\\Navigator.cs"},{"SourceFile":"Net\\eLinkState.cs"},{"SourceFile":"Net\\cLink_A69.cs"},{"SourceFile":"Service\\Service_Crawler.cs"},{"SourceFile":"Service\\Service_Importer.cs"},{"SourceFile":"General\\Cfg.cs"},{"SourceFile":"General\\cTools.cs"},{"SourceFile":"General\\Logger.cs"},{"SourceFile":"Net\\iLink.cs"},{"SourceFile":"Properties\\AssemblyInfo.cs"},{"SourceFile":"UI\\frmReader.cs"},{"SourceFile":"UI\\frmReader.Designer.cs"},{"SourceFile":"Properties\\Resources.Designer.cs"},{"SourceFile":"Properties\\Settings.Designer.cs"},{"SourceFile":"obj\\Debug\\.NETFramework,Version=v4.8.AssemblyAttributes.cs"}],"References":[{"Reference":"C:\\Commesse\\_zPrivate\\A69WD\\Src\\_Lib\\HtmlAgilityPack.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\Microsoft.CSharp.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\mscorlib.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Core.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Data.DataSetExtensions.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Data.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Commesse\\_zPrivate\\A69WD\\Src\\_Lib\\System.Data.SQLite.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Deployment.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Drawing.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Net.Http.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Windows.Forms.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Xml.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.8\\System.Xml.Linq.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""}],"Analyzers":[],"Outputs":[{"OutputItemFullPath":"C:\\Commesse\\_zPrivate\\A69WD\\Src\\A69WD\\bin\\Debug\\A69WD.exe","OutputItemRelativePath":"A69WD.exe"},{"OutputItemFullPath":"","OutputItemRelativePath":""}],"CopyToOutputEntries":[]}
+13 -11
View File
@@ -62,26 +62,25 @@
<Compile Include="Data\cNota.cs" />
<Compile Include="Data\cRac.cs" />
<Compile Include="Data\DBTools.cs" />
<Compile Include="Data\Helper.cs" />
<Compile Include="General\Navigator.cs" />
<Compile Include="Net\cLink_eRac.cs" />
<Compile Include="Net\eLinkState.cs" />
<Compile Include="Net\cLink_A69.cs" />
<Compile Include="Service\Service_Crawler.cs" />
<Compile Include="Service\Service_Importer.cs" />
<Compile Include="Site\cSite_A69.cs" />
<Compile Include="UI\frmDownloader.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="UI\frmDownloader.Designer.cs">
<DependentUpon>frmDownloader.cs</DependentUpon>
</Compile>
<Compile Include="General\Cfg.cs" />
<Compile Include="General\cTools.cs" />
<Compile Include="General\Logger.cs" />
<Compile Include="Net\iLink.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Site\iSiteType.cs" />
<EmbeddedResource Include="UI\frmDownloader.resx">
<DependentUpon>frmDownloader.cs</DependentUpon>
</EmbeddedResource>
<Compile Include="Service\Service_Import_Milu.cs" />
<Compile Include="UI\frmReader.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="UI\frmReader.Designer.cs">
<DependentUpon>frmReader.cs</DependentUpon>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
@@ -92,6 +91,9 @@
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<EmbeddedResource Include="UI\frmReader.resx">
<DependentUpon>frmReader.cs</DependentUpon>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
+3 -1
View File
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using A69WD.UI;
@@ -34,7 +35,8 @@ namespace A69WD {
// Start application:
Logger.Information(" - - - START Application - - - ");
if (DB.DB_ok) {
Application.Run(new frmDownloader());
//Application.Run(new frmDownloader());
Application.Run(new frmReader());
Logger.Information(" - - - STOP Application - - - ");
+294 -6
View File
@@ -867,7 +867,7 @@ namespace A69WD {
/// </summary>
/// <param name="IDrac">Identificativo del racconto desiderato</param>
/// <returns>Ritorna il racconto estratto dal DB</returns>
public cRac GetRacById(long IDrac) {
public cRac GetRacById_Orig(long IDrac) {
lock (objLock) {
cRac rac = null;
List<string> tmp = null;
@@ -1071,14 +1071,11 @@ namespace A69WD {
}
}
/// <summary>
/// Importa il racconto passato sul DB
/// </summary>
/// <param name="rac">Racconto d aimportare</param>
/// <returns>1 se tutto ok, -1 se importazione abortita per errore, -10 se importazione abortita per duplicato, ....</returns>
/// <returns>IDrac se tutto ok, -1 se importazione abortita per errore, -10 se importazione abortita per duplicato, ....</returns>
public int ImportRac(cRac rac) {
lock (objLock) {
// Inizializzo le variabili che occorrono
@@ -1729,7 +1726,298 @@ namespace A69WD {
} // class DBTools
// Metodi ricavati ottimizzando i miei tramite gemini
/// <summary>
/// Ricava il racconto con l'ID indicato leggendolo dal DB
/// </summary>
/// <param name="IDrac">Indice identificativo del racconto voluto</param>
/// <returns>oggetto cRac che rappresenta il racconto</returns>
public cRac GetRacById(long IDrac) {
/*
* Il metodo più pulito e leggibile consiste nell'eseguire una query principale per
* i dati del racconto e poi delle query mirate per popolare le collezioni.
*
* Ecco come implementare la funzione in C# utilizzando Microsoft.Data.Sqlite.
*
* Spiegazione della logica:
* - LEFT JOIN: Ho usato i LEFT JOIN nella query principale così, anche se un racconto
* non ha ancora una trama o un voto assegnato, il record viene comunque recuperato
* correttamente senza errori.
* - Mapping Manuale: Poiché non stiamo usando un ORM pesante (come Entity Framework),
* mappiamo manualmente le colonne del reader alle proprietà dell'oggetto cRac.
* - Gestione Valori Nulli: Noterai il controllo reader["Trama"]?.ToString() ?? "" che
* evita eccezioni nel caso in cui certi campi opzionali siano vuoti nel database.
* - Helper GetList: Per evitare di riscrivere lo stesso codice 3 volte per Autori,
* Tags e Origini, ho creato una piccola funzione di supporto.
*
* Questo approccio è molto performante su SQLite e ti restituisce l'oggetto cRac
* esattamente come lo avevi immaginato, pronto per essere mostrato nella tua
* interfaccia utente.
*
*/
lock (objLock) {
try {
cRac rac = null;
// 1. Query principale: Dati Racconto, Trama e Voto
string queryBase = @"
SELECT r.*, t.Trama, v.Voto as Voto
FROM Racconti r
LEFT JOIN Trame t ON r.IDrac = t.IDrac
LEFT JOIN Links_Rac_Vot lrv ON r.IDrac = lrv.IDrac
LEFT JOIN Voti v ON lrv.IDvot = v.IDvot
WHERE r.IDrac = @IDrac";
using (SQLiteCommand cmd = new SQLiteCommand(queryBase, cn)) {
cmd.Parameters.AddWithValue("@IDrac", IDrac);
using (SQLiteDataReader reader = cmd.ExecuteReader()) {
if (reader.Read()) {
rac = new cRac {
IDrac = Convert.ToInt64(reader["IDrac"]),
Title = reader["Title"].ToString(),
SubTitle = reader["SubTitle"].ToString(),
Creato = DateTime.Parse(reader["Creato"] as string),
Updated = DateTime.Parse(reader["Aggiornato"].ToString()),
Trama = reader["Trama"]?.ToString() ?? "",
Voto = reader["Voto"]?.ToString() ?? "Non Letto"
};
}
}
}
if (rac == null) return null; // Racconto non trovato
// 2. Recupero Autori
rac.Autori = Helper.GetList(cn, "SELECT a.Autore FROM Autori a JOIN Links_Rac_Aut l ON a.IDaut = l.IDaut WHERE l.IDrac = @IDrac", IDrac);
// 3. Recupero Tags
rac.Tags = Helper.GetList(cn, "SELECT t.Tag FROM Tags t JOIN Links_Rac_Tag l ON t.IDtag = l.IDtag WHERE l.IDrac = @IDrac", IDrac);
// 4. Recupero Origini
rac.Origini = Helper.GetList(cn, "SELECT o.Origine FROM Origini o JOIN Links_Rac_Ori l ON o.IDori = l.IDori WHERE l.IDrac = @IDrac", IDrac);
// 5. Recupero Note (Key-Value)
string queryNotes = @"
SELECT n.NKey, n.NVal
FROM Notes n
JOIN Links_Rac_Not l ON n.IDnot = l.IDnot
WHERE l.IDrac = @IDrac";
using (var cmd = new SQLiteCommand(queryNotes, cn)) {
cmd.Parameters.AddWithValue("@IDrac", IDrac);
using (var reader = cmd.ExecuteReader()) {
while (reader.Read()) {
cNota nt = new cNota(reader["NKey"].ToString(), reader["NVal"].ToString());
rac.Notes.Add(nt);
}
}
}
// // 6. Recupero Segnalini
// string querySng = @"
// SELECT s.Linea, s.Segnalino
// FROM Segnalini s
// JOIN Links_Rac_Sng l ON s.IDsng = l.IDsng
// WHERE l.IDrac = @IDrac";
// using (var cmd = new SQLteCommand(querySng, cn) {
// cmd.Parameters.AddWithValue("@IDrac", IDrac);
// using (var reader = cmd.ExecuteReader()) {
// while (reader.Read()) {
// rac.Segnalini.Add(new SegnalinoInfo {
// Linea = Convert.ToInt32(reader["Linea"]),
// Descrizione = reader["Segnalino"].ToString()
// });
// }
// }
// }
return rac;
} catch (Exception ex) {
Logger.Error($"GetRacById({IDrac}) error: {ex.Message}");
return null;
}
}//lock
}
/// <summary>
/// Salva il racconto nel DB
/// </summary>
/// <param name="racconto">Racconto da salvare sul DB</param>
/// <returns>Esito del salvataggio</returns>
public SaveResult SalvaRacconto(cRac racconto) {
lock (objLock) {
using (var transaction = cn.BeginTransaction()) {
try {
// 1. Inserimento Record Principale [Racconti]
var cmdRac = new SQLiteCommand(@"
INSERT INTO Racconti (Title, SubTitle, Creato, Aggiornato)
VALUES (@t, @st, @c, @a);
SELECT last_insert_rowid();",
cn, transaction);
cmdRac.Parameters.AddWithValue("@t", racconto.Title);
cmdRac.Parameters.AddWithValue("@st", racconto.SubTitle);
cmdRac.Parameters.AddWithValue("@c", racconto.Creato);
cmdRac.Parameters.AddWithValue("@a", racconto.Updated);
long newIDrac = (long)cmdRac.ExecuteScalar(); //
racconto.IDrac = newIDrac;
// 2. Salvataggio Relazioni Molti-a-Molti
// Inserimento Trama (Tabella [Trame])
if (!string.IsNullOrWhiteSpace(racconto.Trama)) {
var cmdTra = new SQLiteCommand("INSERT INTO Trame (IDrac, Trama) VALUES (@id, @txt)", cn, transaction);
cmdTra.Parameters.AddWithValue("@id", newIDrac);
cmdTra.Parameters.AddWithValue("@txt", racconto.Trama);
cmdTra.ExecuteNonQuery(); //
}
// Inserimento Voto (Tabella [Links_Rac_Vot])
if (!string.IsNullOrWhiteSpace(racconto.Voto)) {
var cmdVot = new SQLiteCommand("INSERT OR REPLACE INTO Links_Rac_Vot (IDrac, IDvot) VALUES (@r, @v)", cn, transaction);
cmdVot.Parameters.AddWithValue("@r", newIDrac);
cmdVot.Parameters.AddWithValue("@v", racconto.Voto); // IDvot corrisponde al valore rating
cmdVot.ExecuteNonQuery();
}
// 3. Salvataggio Relazioni Molti-a-Molti
// Autori
racconto.Autori.ForEach(autore => {
long idAut = Helper.GetOrCreateId(cn, transaction, "Autori", "Autore", "IDaut", autore);
Helper.ExecuteLink(cn, transaction, "Links_Rac_Aut", "IDaut", newIDrac, idAut);
});
// Tags
racconto.Tags.ForEach(tag => {
long idTag = Helper.GetOrCreateId(cn, transaction, "Tags", "Tag", "IDtag", tag);
Helper.ExecuteLink(cn, transaction, "Links_Rac_Tag", "IDtag", newIDrac, idTag);
});
// Origini Multiple
racconto.Origini.ForEach(origine => {
long idOri = Helper.GetOrCreateId(cn, transaction, "Origini", "Origine", "IDori", origine);
Helper.ExecuteLink(cn, transaction, "Links_Rac_Ori", "IDori", newIDrac, idOri);
});
// Note (Coppia Chiave-Valore)
foreach (var nota in racconto.Notes) {
var idNot = Helper.GetOrCreateNoteId(cn, transaction, nota.Key, nota.Description);
Helper.ExecuteLink(cn, transaction, "Links_Rac_Not", "IDnot", newIDrac, idNot);
}
// // 4. Inserimento Segnalini (Tabella [Segnalini])
// foreach (var sng in racconto.Segnalini) {
// var cmdSng = new SqliteCommand(@"
// INSERT INTO Segnalini (Linea, Segnalino) VALUES (@l, @s);
// SELECT last_insert_rowid();", connection, transaction);
// cmdSng.Parameters.AddWithValue("@l", sng.Linea);
// cmdSng.Parameters.AddWithValue("@s", sng.Descrizione);
// long idSng = (long)cmdSng.ExecuteScalar();
//
// // Link Segnalino al Racconto
// ExecuteLink(connection, transaction, "Links_Rac_Sng", "IDsng", newIDrac, idSng);
// }
transaction.Commit();
Logger.Information($"Inserito racconto {newIDrac}");
return SaveResult.Success;
} catch (Exception ex) {
transaction.Rollback();
Logger.Information($"Errore SQL durante l'inserimento del racconto racconto {racconto.Title}");
return SaveResult.GenericError;
}
}//transaction
}//lock
}
#region Reader function: GetReport, GetRandomIDrac
public long GetNumRacs(string TagFilter = "") {
string query = "SELECT count (IDrac) FROM Racconti";
if (TagFilter != "") { query = "SELECT count (Racconti.IDrac) AS NUM FROM Racconti, Links_Rac_Vot, Voti WHERE Racconti.IDrac=Links_Rac_Vot.IDrac AND Links_Rac_Vot.IDvot=Voti.IDvot AND Voti.Voto='" + TagFilter + "'"; }
return Helper.ExecuteScalar(cn,query,objLock);
}
private long GetNumOri() { return Helper.CountSimpleRecord(cn, "Links_Rac_Ori", "IDori", objLock); }
private long GetNumAuts() { return Helper.CountSimpleRecord(cn, "Links_Rac_Aut", "IDaut", objLock); }
private long GetNumTags() { return Helper.CountSimpleRecord(cn, "Links_Rac_Tag", "IDtag", objLock); }
private long GetNumVot() { return Helper.CountSimpleRecord(cn, "Links_Rac_Vot", "IDvot", objLock); }
/// <summary>
/// ritorna il riepilogo del DB:
/// </summary>
/// <returns>Stringa con il riepilogo del DB</returns>
public string Report {
get {
string report = "A69WD:\r\n\r\n";
report += string.Format("L'archivio contiene {0} racconti:\r\n", GetNumRacs().ToString("000"));
report += string.Format("> {0} non letti\r\n", GetNumRacs("Non Letto").ToString("000"));
report += string.Format("> {0} sorgenti\r\n", GetNumOri().ToString("00"));
report += string.Format("> {0} autori\r\n", GetNumAuts().ToString("000"));
report += string.Format("> {0} tags\r\n", GetNumTags().ToString("000"));
report += string.Format("> {0} voti\r\n", GetNumVot().ToString("000"));
return report;
}
}
/// <summary>
/// Metodo per trovare un identificativo di un racconto scelto casualmente fra quelli che soddisfano il filtro indicato.
/// </summary>
/// <param name="queryFilter">Eventuale query per filtrare i racconti selezionabili</param>
/// <returns>Identificativo del racconto scelto casualmente</returns>
public long GetRandomIDrac(string queryFilter = "") {
//long ID = -3;
//SQLiteDataReader rdr = null;
//lock (objLock) {
//try {
//// Preparo i comandi SQL da usare:
//string query = "SELECT Links_Rac_Vot.IDrac AS IDrac FROM Links_Rac_Vot, Voti WHERE Voti.Voto='Non Letto' AND Links_Rac_Vot.IDVot=Voti.IDvot ORDER BY RANDOM() LIMIT 1";
//if (!string.IsNullOrEmpty(queryFilter)) query = string.Format("SELECT Links_Rac_Vot.IDrac AS IDrac FROM Links_Rac_Vot, Voti WHERE Links_Rac_Vot.IDVot=Voti.IDvot AND {0} ORDER BY RANDOM() LIMIT 1", queryFilter);
//SQLiteCommand cmd = new SQLiteCommand(query, cn);
//rdr = cmd.ExecuteReader();
//while (rdr.Read()) { ID = (long)rdr["IDrac"]; }
//rdr.Close();
//rdr = null;
//} catch (Exception ex) {
//Logger.Error(string.Format("Error Code: {0} - Errore in {1}.{2}: {3}", "ER00070", System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.FullName, System.Reflection.MethodInfo.GetCurrentMethod().Name, ex.Message));
//ID = -2;
//}
//}
//return ID;
string query = "SELECT Links_Rac_Vot.IDrac AS IDrac FROM Links_Rac_Vot, Voti WHERE Voti.Voto='Non Letto' AND Links_Rac_Vot.IDVot=Voti.IDvot ORDER BY RANDOM() LIMIT 1";
if (!string.IsNullOrEmpty(queryFilter)) query = string.Format("SELECT Links_Rac_Vot.IDrac AS IDrac FROM Links_Rac_Vot, Voti WHERE Links_Rac_Vot.IDVot=Voti.IDvot AND {0} ORDER BY RANDOM() LIMIT 1", queryFilter);
return Helper.ExecuteScalar(cn, query, objLock);
}
public int UpdateVoto(long IDrac, string NewVoto) {
Logger.Warning("Funzione 'UpdateVoto(IDrac, NewVoto) ancora da fare.");
return -1;
}
#endregion
} //class DBTools
}//namespace A69WD
+143
View File
@@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SQLite;
namespace A69WD.Data {
/*
*
* Questo file contiene i metodi statici per gestire le tabelle di lookup e i collegamenti.
* Nota che questi metodi accettano SqliteTransaction per operare all'interno del contesto della transazione principale.
*
* Vantaggi di questa suddivisione:
* - Helper statico: Non serve istanziare nulla per usare le funzioni di utilità.
* - Manutenibilità: Se cambi il modo in cui cerchi un autore, lo cambi in un solo punto (Helper.cs).
* - Transazioni pulite: DBTools.cs si occupa solo di aprire la transazione e chiamare in ordine i "mattoncini" forniti dall'Helper.
*
*/
public static class Helper {
// Ottiene ID o crea nuovo record per Autori, Tags, Origini
public static long GetOrCreateId(SQLiteConnection conn, SQLiteTransaction trans, string table, string colName, string idCol, string value) {
var cmdSelect = new SQLiteCommand($"SELECT {idCol} FROM {table} WHERE {colName} = @v", conn, trans);
cmdSelect.Parameters.AddWithValue("@v", value);
var result = cmdSelect.ExecuteScalar();
if (result != null) return (long)result;
var cmdInsert = new SQLiteCommand($"INSERT INTO {table} ({colName}) VALUES (@v); SELECT last_insert_rowid();", conn, trans);
cmdInsert.Parameters.AddWithValue("@v", value);
return (long)cmdInsert.ExecuteScalar();
}
// Helper specifico per le Note (Key-Value)
public static long GetOrCreateNoteId(SQLiteConnection conn, SQLiteTransaction trans, string key, string val) {
var cmdSelect = new SQLiteCommand("SELECT IDnot FROM Notes WHERE NKey = @k AND NVal = @v", conn, trans);
cmdSelect.Parameters.AddWithValue("@k", key);
cmdSelect.Parameters.AddWithValue("@v", val);
var result = cmdSelect.ExecuteScalar();
if (result != null) return (long)result;
var cmdInsert = new SQLiteCommand("INSERT INTO Notes (NKey, NVal) VALUES (@k, @v); SELECT last_insert_rowid();", conn, trans);
cmdInsert.Parameters.AddWithValue("@k", key);
cmdInsert.Parameters.AddWithValue("@v", val);
return (long)cmdInsert.ExecuteScalar();
}
// Crea il legame nelle tabelle Links_...
public static void ExecuteLink(SQLiteConnection conn, SQLiteTransaction trans, string table, string targetIdCol, long idRac, long targetId) {
var cmd = new SQLiteCommand($"INSERT OR IGNORE INTO {table} (IDrac, {targetIdCol}) VALUES (@r, @t)", conn, trans);
cmd.Parameters.AddWithValue("@r", idRac);
cmd.Parameters.AddWithValue("@t", targetId);
cmd.ExecuteNonQuery();
}
// Helper per la lettura rapida di liste di stringhe
public static List<string> GetList(SQLiteConnection conn, string sql, long IDrac) {
var list = new List<string>();
using (var cmd = new SQLiteCommand(sql, conn)) {
cmd.Parameters.AddWithValue("@IDrac", IDrac);
using (var reader = cmd.ExecuteReader()) {
while (reader.Read()) list.Add(reader.GetValue(0).ToString());
}
}
return list;
}
/// <summary>
/// Esegue una query che ritorna un scalare
/// </summary>
/// <param name="query">Query da eseguire sul DB</param>
/// <returns>Scalare ritornato dalla query</returns>
public static long ExecuteScalar(SQLiteConnection conn, string query, object objLock) {
lock (objLock) {
try {
// Eseguo il comando
System.Data.SQLite.SQLiteCommand cm = new SQLiteCommand(query, conn);
long res = (long)cm.ExecuteScalar(System.Data.CommandBehavior.SingleResult);
return res;
} catch (Exception ex) {
if (ex.Message == "Object reference not set to an instance of an object.") {
// nessun record trovato che soddisfi la query
return 0;
} else {
Logger.Error(string.Format("Error Code: {0} - Errore in {1}.{2}: {3}", "ER00100", System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.FullName, System.Reflection.MethodInfo.GetCurrentMethod().Name, ex.Message));
return -1;
}
}
}
} // ExecuteScalar
/// <summary>
/// Conteggia il numero di record nella colonna indicata contenuti nella tabella indicata.
/// </summary>
/// <param name="conn">Connessione al DB</param>
/// <param name="tableName">Nome della tabella in cui fare la query</param>
/// <param name="columName">Nome del campo da contare</param>
/// <param name="objLock">Oggetto per il lock fra i thread</param>
/// <returns>Il numero di record trovati, oppure un codice negativo in caso di errore.</returns>
public static long CountSimpleRecord(SQLiteConnection conn, string tableName, string columName, object objLock) {
// Ricreo la query:
string query = $"SELECT count(Distinct {tableName}.{columName}) AS NUM FROM {tableName}";
lock (objLock) {
try {
// Eseguo il comando
System.Data.SQLite.SQLiteCommand cm = new SQLiteCommand(query, conn);
long res = (long)cm.ExecuteScalar(System.Data.CommandBehavior.SingleResult);
return res;
} catch (Exception ex) {
if (ex.Message == "Object reference not set to an instance of an object.") {
// nessun record trovato che soddisfi la query
return 0;
} else {
Logger.Error(string.Format("Error Code: {0} - Errore in {1}.{2}: {3}", "ER00100", System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.FullName, System.Reflection.MethodInfo.GetCurrentMethod().Name, ex.Message));
return -1;
}
}
}
}//CountSimpleRecord
}//class Helper
public enum SaveResult {
Success = 0,
DatabaseConnectionError = 1,
MainRecordInsertError = 2,
TramaInsertError = 3,
RelationInsertError = 4,
SegnaliniInsertError = 5,
GenericError = 99
}
}//namespace A69WD.Data
+4
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace A69WD.Data {
@@ -11,7 +12,10 @@ namespace A69WD.Data {
/// </summary>
public class cNota {
[XmlAttribute]
public string Key { get; set; }
[XmlAttribute]
public string Description { get; set; }
/// <summary>
+286 -94
View File
@@ -1,103 +1,132 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
namespace A69WD.Data {
/// <summary>
/// Rappresenta un singolo racconto all'interno del database ATDB.
/// Questa classe mappa la tabella [Racconti] e tutte le sue relazioni correlate.
/// </summary>
public class cRac {
#region Costruttori
#region Propietà: IDrac, Title, SubTitle, Creato, Updated, Trama, Voto, Autori, Tags, Origini, Notes
#region --- DATI BASE (Tabella [Racconti]) ---
/// <summary>
/// Costruttore
/// Identificativo univoco del racconto
/// Default: -1
/// Mappa [IDrac]
/// </summary>
public cRac() { isValid = false; }
public long IDrac { get; set; } = -1;
/// <summary>
/// Costruttore per un racconto nullo, utilizzato per mostrare eventuali errori o messaggi
/// <summary> Titolo del racconto
/// Default: 'Unknown'
/// Mappa [Title]
/// </summary>
/// <param name="title">Stringa che si vuole visualizzare come titolo del racconto</param>
/// <param name="trama">Stringa che si vuole visualizzare come testo del racconto</param>
public cRac(string title, string trama) {
this.IDrac = 0;
this.Title = title;
this.Trama = "\n\n" + trama + "\n\n";
this.Creato = DateTime.Now;
this.Updated = DateTime.Now;
this.isValid = true;
}
public string Title { get; set; } = "Unknown";
#endregion
#region Propietà e Fields:
/// <summary>
/// Identificativo del racconto
/// </summary>
public long IDrac = -1;
/// <summary>
/// Titolo del racconto
/// </summary>
public string Title = null;
/// <summary>
/// <summary>
/// Sottotitolo del racconto
/// Default: ''
/// Mappa [SubTitle]
/// </summary>
public string SubTitle = null;
/// <summary>
/// Elenco dei tag (ex Props) associati al racconto
/// </summary>
public List<string> Tags = null;
/// <summary>
/// Note aggiuntive associate al racconto, sono del tipo: {Key} - {Descrizione}
/// Un tipo di nota sempre presente è la "Src" che individua la sorgente (link del file downloadato) da dove ho preso il racconto
/// Devo poter aver più source e quindi abbandono la struttura dizionario per avere una lista di coppie di stringhe
/// </summary>
//public System.Collections.Generic.Dictionary<string, string> Notes = null;
public List<cNota> Notes = null;
/// <summary>
/// Elenco degli autori del racconto
/// </summary>
public List<string> Autori = null;
/// <summary>
/// Elenco delle origini del racconto
/// </summary>
public List<string> Origini = null;
/// <summary>
/// Voto assegnato al racconto
/// </summary>
public string Voto = null;
/// <summary>
/// Trama del racconto
/// </summary>
public string Trama = null;
public string SubTitle { get; set; } = string.Empty;
/// <summary>
/// Data di creazione/pubblicazione del racconto o in mancanza di essa, data di inserimento del racconto in ATDB
/// Default: DateTime.MinValue
/// Mappa [Creato]
/// </summary>
public DateTime Creato = DateTime.MinValue;
public DateTime Creato { get; set; } = DateTime.MinValue;
/// <summary>
/// Data dell'ultimo update del racconto.
/// Inizialmente sarà la data di inserimento in ATDB, poi sarà la data dell'ultima lettura o dell'ultima iterazione con il racconto stesso.
/// Data dell'ultima modifica
/// Default: DateTime.MinValue
/// Mappa [Aggiornato]
/// </summary>
public DateTime Updated = DateTime.MinValue;
public DateTime Updated { get; set; } = DateTime.MinValue;
#endregion
#region --- CONTENUTO (Relazione 1-1 con Tabella [Trame]) ---
/// <summary>
/// Testo completo della trama del racconto
/// Default: ''
/// Mappa [Trama]
/// </summary>
public string Trama { get; set; } = string.Empty;
#endregion
#region --- CONTENUTO (Relazione 1-1 con Tabella [Voti]) ---
/// <summary>
/// Etichetta testuale del voto assegnato al racconto (es. 'Bello')
/// Default: 'Non Letto'
/// Mappa [Voto]
/// </summary>
public string Voto { get; set; } = "Non Letto";
#endregion
#region --- RELAZIONI MOLTI-A-MOLTI (Tramite tabelle Links_Rac_...) ---
/// <summary>
/// Elenco dei nomi degli autori associati al racconto.
/// Default: null
/// Mappa la relazione tramite [Links_Rac_Aut] con la tabella [Autori].
/// </summary>
[XmlArray("Autori")]
[XmlArrayItem("Autore")]
public List<string> Autori { get; set; } = new List<string>();
/// <summary>
/// Elenco dei tag o proprietà associati (es. Genere, Mood).
/// Default: null
/// Mappa la relazione tramite [Links_Rac_Tag] con la tabella [Tags].
/// </summary>
[XmlArray("Tags")]
[XmlArrayItem("Tag")]
public List<string> Tags { get; set; } = new List<string>();
/// <summary>
/// Elenco delle sorgenti o origini da cui proviene il racconto.
/// Default: null
/// Mappa la relazione tramite [Links_Rac_Ori] con la tabella [Origini].
/// </summary>
[XmlArray("Origini")]
[XmlArrayItem("Origine")]
public List<string> Origini { get; set; } = new List<string>();
/// <summary>
/// Note informative strutturate come coppia Chiave-Valore (es. Link, Resume).
/// Note aggiuntive associate al racconto, sono del tipo: {Key} - {Descrizione}
/// Un tipo di nota sempre presente è la "Src" che individua la sorgente (link del file downloadato) da dove ho preso il racconto
/// Devo poter aver più source e quindi abbandono la struttura dizionario per avere una lista di coppie di stringhe
/// Default: null
/// Mappa la relazione tramite [Links_Rac_Not] con la tabella [Notes].
/// </summary>
//public Dictionary<string, string> Note { get; set; } = new Dictionary<string, string>();
[XmlArray("Notes")]
[XmlArrayItem("Nota")]
public List<cNota> Notes { get; set; } = new List<cNota>();
#endregion
#endregion
#region Metodi: strIDrac, strTags, fullTitle
#region Metodi: str..., fullTitle, IsValid, Validate, FileRacName
#region Conversioni a stringa dei campi non stringhe:
/// <summary>
/// ID del racconto espresso in formato testuale
@@ -164,6 +193,8 @@ namespace A69WD.Data {
}
}
#endregion
/// <summary>
/// Indica se il racconto è valido o no (nullo)
/// </summary>
@@ -213,18 +244,92 @@ namespace A69WD.Data {
}
} catch (Exception ex) { Logger.Log(new object[] { "Error Code:val001", "Errore in ATWD.Rac.Validate", ex }); }
try {
//if (!isValid) {
// Logger.Warning("NOT VALIDATE: " + strIDrac + " - " + fullTitle);
//}
} catch (Exception ex) { Logger.Error(string.Format("Error Code: {0} - Errore in {1}.{2}: {3}", "ER375278", System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.FullName, System.Reflection.MethodInfo.GetCurrentMethod().Name, ex.Message)); }
//try {
// //if (!isValid) {
// // Logger.Warning("NOT VALIDATE: " + strIDrac + " - " + fullTitle);
// //}
//} catch (Exception ex) { Logger.Error(string.Format("Error Code: {0} - Errore in {1}.{2}: {3}", "ER375278", System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.FullName, System.Reflection.MethodInfo.GetCurrentMethod().Name, ex.Message)); }
return isValid;
}
/// <summary>
/// Crea un nome univoco per il file htm che rappresenta un racconto.
/// Rac00000.htm
/// </summary>
public static string FileRacName(long RacID) { return "Rac" + RacID.ToString("00000") + ".htm"; }
#endregion
#region Costruttori
/// <summary>
/// Costruttore
/// </summary>
public cRac() { isValid = false; }
/// <summary>
/// Costruttore per un racconto nullo, utilizzato per mostrare eventuali errori o messaggi
/// </summary>
/// <param name="title">Stringa che si vuole visualizzare come titolo del racconto</param>
/// <param name="trama">Stringa che si vuole visualizzare come testo del racconto</param>
public cRac(string title, string trama) {
this.IDrac = 0;
this.Title = title;
this.Trama = "\n\n" + trama + "\n\n";
this.Creato = DateTime.Now;
this.Updated = DateTime.Now;
this.isValid = true;
}
#endregion
#region Serializzazione su file XML
/// <summary>
/// Serializza l'oggetto cRac in xml
/// </summary>
/// <returns>Testo xml che serializza il cRac</returns>
public string ToXml() {
XmlSerializer serializer = new XmlSerializer(typeof(cRac));
StringWriter sw = new StringWriter();
serializer.Serialize(sw, this);
return sw.ToString();
}
/// <summary>
/// Deserializza l'oggetto cRac dal formato xml
/// </summary>
/// <param name="xml">Testo xml che serializza il cRac</param>
/// <returns>cRac che rappresenta il racconto ricavato dal testo xml</returns>
public static cRac FromXml(string xml) {
XmlSerializer serializer = new XmlSerializer(typeof(cRac));
StringReader sr = new StringReader(xml);
return (cRac)serializer.Deserialize(sr);
}
/// <summary>
/// Salva il cRac come file xml
/// </summary>
/// <param name="path">Path dove salvare</param>
public void SaveToXmlFile(string path) {
File.WriteAllText(path, ToXml());
}
/// <summary>
/// Carica il cRac dal file XML indicato
/// </summary>
/// <param name="path">Path da dove caricare</param>
/// <returns>cRac che rappresenta il racconto ricavato dal file xml</returns>
public static cRac LoadFromXmlFile(string path) {
string xml = File.ReadAllText(path);
return FromXml(xml);
}
#endregion
/// <summary>
/// Crea il file html che rappresenta il racconto.
@@ -329,24 +434,6 @@ namespace A69WD.Data {
}
/// <summary>
/// Crea un nome univoco per il file htm che rappresenta un racconto.
/// Rac00000.htm
/// </summary>
public static string FileRacName(long RacID) { return "Rac" + RacID.ToString("00000") + ".htm"; }
/// <summary>
/// Stringa di separazione fra i vari campi quando esprimo un elenco tramite una singola stringa
/// </summary>
private const string SEPARATORETXT = " -|- ";
/// <summary>
/// Esprime la data indicata con una stringa opportunamente formattata
/// </summary>
/// <param name="dt">DateTime da esprimere in testo</param>
/// <returns>Il testo opportunamente formattato</returns>
private string DateFormat(DateTime dt) { return dt.ToString("yyyy/MM/dd"); }
/// <summary>
/// Override per esprimere in modo formale un racconto con una singola stringa.
/// </summary>
@@ -402,6 +489,19 @@ namespace A69WD.Data {
}
/// <summary>
/// Stringa di separazione fra i vari campi quando esprimo un elenco tramite una singola stringa
/// </summary>
private const string SEPARATORETXT = " -|- ";
/// <summary>
/// Esprime la data indicata con una stringa opportunamente formattata
/// </summary>
/// <param name="dt">DateTime da esprimere in testo</param>
/// <returns>Il testo opportunamente formattato</returns>
private string DateFormat(DateTime dt) { return dt.ToString("yyyy/MM/dd"); }
/// <summary>
/// Esprime il racconto in formato HTML formale.
/// </summary>
@@ -825,6 +925,98 @@ namespace A69WD.Data {
}
}
public static string EsportaInHtml(cRac racconto, string cartellaDestinazione) {
// Crea il nome del file pulendo il titolo da caratteri non validi
string nomeFile = string.Join("_", racconto.Title.Split(Path.GetInvalidFileNameChars()));
string pathCompleto = Path.Combine(cartellaDestinazione, $"{racconto.strIDrac}_{nomeFile}.html");
StringBuilder sb = new StringBuilder();
try {
// Inizio Documento HTML
sb.AppendLine("<!DOCTYPE html>");
sb.AppendLine("<html lang='it'>");
sb.AppendLine("<head>");
sb.AppendLine(" <meta charset='UTF-8'>");
sb.AppendLine($" <title>{racconto.Title}</title>");
// CSS Incorporato per uno stile elegante (Bookish Style)
sb.AppendLine(" <style>");
sb.AppendLine(" body { font-family: 'Georgia', serif; line-height: 1.6; color: #333; max-width: 800px; margin: 40px auto; padding: 20px; background-color: #f4f1ea; }");
sb.AppendLine(" .container { background: white; padding: 50px; box-shadow: 0 0 10px rgba(0,0,0,0.1); border-radius: 5px; }");
sb.AppendLine(" h1 { color: #2c3e50; text-align: center; margin-bottom: 5px; font-variant: small-caps; }");
sb.AppendLine(" .subtitle { text-align: center; font-style: italic; color: #7f8c8d; margin-bottom: 30px; }");
sb.AppendLine(" .metadata { border-top: 1px solid #eee; border-bottom: 1px solid #eee; padding: 15px 0; margin-bottom: 30px; font-size: 0.9em; display: flex; flex-wrap: wrap; gap: 20px; }");
sb.AppendLine(" .meta-item { color: #555; }");
sb.AppendLine(" .meta-label { font-weight: bold; color: #2c3e50; }");
sb.AppendLine(" .trama { white-space: pre-wrap; text-align: justify; font-size: 1.1em; }");
sb.AppendLine(" .tags { margin-top: 40px; }");
sb.AppendLine(" .tag { background: #e0e0e0; padding: 3px 10px; border-radius: 15px; font-size: 0.8em; margin-right: 5px; display: inline-block; }");
sb.AppendLine(" .footer { margin-top: 50px; font-size: 0.8em; text-align: center; color: #bdc3c7; border-top: 1px solid #eee; padding-top: 10px; }");
sb.AppendLine(" @media print { body { background: white; } .container { box-shadow: none; } }");
sb.AppendLine(" </style>");
sb.AppendLine("</head>");
sb.AppendLine("<body>");
sb.AppendLine(" <div class='container'>");
// Intestazione
sb.AppendLine($" <h1>{racconto.Title}</h1>");
if (!string.IsNullOrEmpty(racconto.SubTitle))
sb.AppendLine($" <p class='subtitle'>{racconto.SubTitle}</p>");
// Metadata (Autori, Voto, Origini)
sb.AppendLine(" <div class='metadata'>");
sb.AppendLine($" <div class='meta-item'><span class='meta-label'>Autore:</span> {string.Join(", ", racconto.Autori)}</div>");
sb.AppendLine($" <div class='meta-item'><span class='meta-label'>Data:</span> {racconto.Creato}</div>");
sb.AppendLine($" <div class='meta-item'><span class='meta-label'>Voto:</span> {racconto.Voto})</div>");
if (racconto.Origini.Any())
sb.AppendLine($" <div class='meta-item'><span class='meta-label'>Sorgente:</span> {string.Join(", ", racconto.Origini)}</div>");
sb.AppendLine(" </div>");
// Corpo del racconto (Trama)
sb.AppendLine(" <div class='trama'>");
sb.AppendLine(racconto.Trama); // Preserva i ritorni a capo grazie a white-space: pre-wrap
sb.AppendLine(" </div>");
// Note (se presenti)
if (racconto.Notes.Any()) {
sb.AppendLine(" <div style='margin-top: 30px; padding: 15px; background: #f9f9f9; border-left: 5px solid #2c3e50;'>");
sb.AppendLine(" <h3 style='margin-top:0;'>Note</h3>");
foreach (var nota in racconto.Notes)
sb.AppendLine($" <p><strong>{nota.Key}:</strong> {nota.Description}</p>");
sb.AppendLine(" </div>");
}
// Tags
if (racconto.Tags.Any()) {
sb.AppendLine(" <div class='tags'>");
racconto.Tags.Sort();
//foreach (var tag in racconto.Tags)
// sb.AppendLine($" <span class='tag'>#{tag}</span>");
racconto.Tags.ForEach(t => sb.AppendLine($" <span class='tag'>#{t}</span>"));
sb.AppendLine(" </div>");
}
// Footer
sb.AppendLine(" <div class='footer'>");
sb.AppendLine($" Esportato da ATDB Collection - {DateTime.Now:dd/MM/yyyy HH:mm}");
sb.AppendLine(" </div>");
sb.AppendLine(" </div>"); // Fine container
sb.AppendLine("</body>");
sb.AppendLine("</html>");
// Scrittura su file
File.WriteAllText(pathCompleto, sb.ToString());
} catch (Exception ex) { Logger.Error("A69 Export - " + ex.Message); }
return pathCompleto;
}
} // class cRac
}//namespace A69WD.Data
+18
View File
@@ -119,6 +119,12 @@ namespace A69WD {
public virtual string PathApplicationRoot { get { return applicationRootPath; } set { applicationRootPath = value; } }
private string applicationRootPath;
/// <summary>
/// Path della cartella con i file da importare.
/// </summary>
public virtual string PathImport { get { return pathImport; } set { pathImport = value; } }
private string pathImport;
/// <summary>
/// Nome del database usato dall'applicazione [DEPRECATE].
/// </summary>
@@ -150,6 +156,12 @@ namespace A69WD {
public virtual int nThread { get { return nthread; } set { nthread = value; } }
private int nthread;
/// <summary>
/// Numero di file massimo da gestire
/// </summary>
public virtual int MaxNumFile { get { return maxNumFile; } set { maxNumFile = value; } }
private int maxNumFile;
#endregion
@@ -225,6 +237,12 @@ namespace A69WD {
}
private string pathAnalizadedRaccFile = "";
public virtual string PathSrcDone => System.IO.Path.Combine(PathDownloadWorking, "Done");
public virtual string PathIdxDone => System.IO.Path.Combine(PathDownloadWorking, "Idx");
public virtual string PathSrcErrOnExtract => System.IO.Path.Combine(PathDownloadWorking, "ErrOnExtract");
public virtual string PathSrcErrOnSave => System.IO.Path.Combine(PathDownloadWorking, "ErrOnSave");
#endregion
+186
View File
@@ -0,0 +1,186 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using A69WD.Data;
namespace A69WD {
public class Navigator {
#region Fields: buffer, indice, DB
/// <summary>
/// Array interno che mi memorizza gli ID degli ultimi racconti visualizzati
/// </summary>
private long[] buffer = null;
/// <summary>
/// Indice del racconto attualmente visualizzato
/// </summary>
private long indice = 0;
/// <summary>
/// Interfaccia con il database
/// </summary>
private DBTools DB = null;
#endregion
#region Costrutore
/// <summary>
/// Costruttore
/// </summary>
/// <param name="size">Dimensione del buffer, cioè numero di racconti di cui tener memoria</param>
public Navigator(int size) {
buffer = new long[size];
for (int i = 0; i < size; i++) { buffer[i] = -1; }
DB = App.DB;
}
#endregion
#region Metodi di navigazione:
/// <summary>
/// Ritorna l'ID del racconto attualmente visualizzato
/// 0 indica la pagina home con il riepilogo del ATDB
/// </summary>
public long ActualID { get { return buffer[indice]; } }
/// <summary>
/// Ritorna il riepilogo dell'archivio
/// </summary>
public string Home { get { return DB.Report; } }
/// <summary>
/// Ritorna il testo del racconto successivo a quello visualizzato attualmente, nel buffer di navigazione
/// </summary>
public string Forward {
get {
// Aggiorno indice attuale:
long tmp = indice + 1;
if (tmp >= buffer.Length) tmp = 0;
if (buffer[tmp] != -1) indice = tmp;
// Ritorno il testo del racconto:
return DB.GetRacById(buffer[indice]).ToString();
}
}
/// <summary>
/// Ritorna il testo del racconto precedente a quello visualizzato attualmente, nel buffer di navigazione
/// </summary>
public string Backward {
get {
// Aggiorno indice attuale:
long tmp = indice - 1;
if (tmp < 0) tmp = buffer.Length - 1;
if (buffer[tmp] != -1) indice = tmp;
// Ritorno il testo del racconto:
return DB.GetRacById(buffer[indice]).ToString();
}
}
/// <summary>
/// Ritorna il testo del racconto con ID successivo a quello visualizzato attualmente
/// </summary>
public string Next {
get {
// Aggiorno indice attuale:
long newID = buffer[indice];
if (newID > 0) {
newID++;
return SwitchTo(newID);
} else {
return Refresh;
}
}
}
/// <summary>
/// Ritorna il testo del racconto con ID precedente a quello visualizzato attualmente
/// </summary>
public string Previous {
get {
// Aggiorno indice attuale:
long newID = buffer[indice];
if (newID > 1) {
newID--;
return SwitchTo(newID);
} else {
return Refresh;
}
}
}
/// <summary>
/// Ricarica il testo del racconto attuale
/// </summary>
public string Refresh {
get {
if (buffer[indice] > 0) {
return DB.GetRacById(buffer[indice]).ToString();
} else { return Home; }
}
}
/// <summary>
/// Ritorna il testo di un racconto scelto casualmente nel DB
/// </summary>
public string RandomRac {
get {
// Aggiorno indice attuale:
indice++;
if (indice >= buffer.Length) indice = 0;
buffer[indice] = DB.GetRandomIDrac();
// Ritorno il testo del racconto:
return DB.GetRacById(buffer[indice]).ToString();
}
}
/// <summary>
/// Ritorna l'ID del nuovo racconto prendendo quello indicato
/// </summary>
/// <param name="NewIdRac">ID del racconto da visualizzare</param>
/// <returns></returns>
public string SwitchTo(long NewIdRac) {
// Aggiorno indice attuale:
indice++;
if (indice >= buffer.Length) indice = 0;
buffer[indice] = NewIdRac;
// Ritorno il testo del racconto:
cRac r = DB.GetRacById(buffer[indice]);
if (r != null) {
return r.ToString();
} else {
return $"Indice {NewIdRac} non valido";
}
}
/// <summary>
/// Ricarica il testo del racconto attuale
/// </summary>
public string UpdateVoto(string newVoto) {
long id = buffer[indice];
if (id > 0) {
DB.UpdateVoto(id, newVoto);
return DB.GetRacById(id).ToString();
} else { return Home; }
}
#endregion
}//class Navigator
}//namespace A69WD
+136 -54
View File
@@ -1,13 +1,14 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
namespace A69WD {
public class cTools {
@@ -47,6 +48,12 @@ namespace A69WD {
}
public static void SaveToFile(string txtHTML, string filePath, string fileName) {
System.IO.StreamWriter tsw = new System.IO.StreamWriter(System.IO.Path.Combine(filePath, fileName), false, Encoding.UTF8);
tsw.Write(txtHTML);
}
/// <summary>
/// Metodo che legge un testo da file (UTF8)
/// </summary>
@@ -470,7 +477,7 @@ namespace A69WD {
/// <param name="txt">Testo da formattare</param>
/// <param name="eliminaTagHtml">Abilitare l'eliminazione di tutti i generici tag </param>
/// <returns>Testo formattato</returns>
public static string FormattaTesto(string txt, bool eliminaTagHtml) {
public static string FormattaTesto(string txt, bool eliminaTagHtml = true) {
try {
// Analisi dei tag prima di levarli:
string testo = Regex.Replace(txt, @"<P>", "\r\n", RegexOptions.IgnoreCase);
@@ -497,6 +504,20 @@ namespace A69WD {
}
// Special Entities dal sito https://www.htmlhelp.com/reference/html40/entities/special.html
// More can be added, see http://hotwired.lycos.com/webmonkey/reference/special_characters/
// Correggo risultato togliendo doppie linee e caratteri html:
testo = Regex.Replace(testo, @"&nbsp;", " ", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&bull;", " * ", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&hellip;", "…", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&trade;", "(tm)", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&copy;", "(c)", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&reg;", "(r)", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&frasl;", "/", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&agrave;", "à", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&egrave;", "è", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&igrave;", "ì", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&ograve;", "ò", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&ugrave;", "ù", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&quot;", "\"", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&amp;", "&", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&lt;", "<", RegexOptions.IgnoreCase);
@@ -521,8 +542,13 @@ namespace A69WD {
testo = Regex.Replace(testo, @"&euro;", "€", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#34;", "\"", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#38;", "&", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#39;", "'", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#60;", "<", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#62;", ">", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#268;", "Č", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#269;", "č", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#282;", "Ě", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#283;", "ě", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#339;", "œ", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#353;", "š", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#376;", "Ÿ", RegexOptions.IgnoreCase);
@@ -537,10 +563,13 @@ namespace A69WD {
testo = Regex.Replace(testo, @"&#8221;", "”", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8222;", "„", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8224;", "†", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8230;", "…", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8232;", " ", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8240;", "‰", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8249;", "‹", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8250;", "›", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8364;", "€", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#61516;", " ", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#x22;", "\"", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#x26;", "&", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#x3C;", "<", RegexOptions.IgnoreCase);
@@ -564,58 +593,17 @@ namespace A69WD {
testo = Regex.Replace(testo, @"&#x203A;", "›", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#x20AC;", "€", RegexOptions.IgnoreCase);
// Correggo risultato togliendo doppie linee e caratteri html:
testo = Regex.Replace(testo, @"&nbsp;", " ", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&bull;", " * ", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&lt;", "<", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&rsaquo;", " > ", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&gt;", ">", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&trade;", "(tm)", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&copy;", "(c)", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&reg;", "(r)", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&frasl;", "/", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&agrave;", "à", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&egrave;", "è", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&igrave;", "ì", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&ograve;", "ò", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&ugrave;", "ù", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8211;", "–", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8212;", "—", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8216;", "‘", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8217;", "’", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8220;", "“", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8221;", "”", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8230;", "…", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#268;", "Č", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#269;", "č", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#282;", "Ě", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#283;", "ě", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#8232;", " ", RegexOptions.IgnoreCase);
testo = Regex.Replace(testo, @"&#61516;", " ", RegexOptions.IgnoreCase);
// // Remove all others. More can be added, see
// // http://hotwired.lycos.com/webmonkey/reference/special_characters/
// testo = Regex.Replace(testo, @"&(.{2,6});", string.Empty, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
//// Correggo accenti per formati sbagliati:
//while (testo.Contains("à")) { testo = testo.Replace("à", "à"); }
//while (testo.Contains("è")) { testo = testo.Replace("è", "è"); }
//while (testo.Contains("é")) { testo = testo.Replace("é", "é"); }
//while (testo.Contains("ì")) { testo = testo.Replace("ì", "ì"); }
//while (testo.Contains("ò")) { testo = testo.Replace("ò", "ò"); }
//while (testo.Contains("ù")) { testo = testo.Replace("ù", "ù"); }
//while (testo.Contains("’")) { testo = testo.Replace("’", "'"); }
// Correggo accenti per formati sbagliati:
while (testo.Contains("à")) { testo = testo.Replace("à", "à"); }
while (testo.Contains("è")) { testo = testo.Replace("è", "è"); }
while (testo.Contains("é")) { testo = testo.Replace("é", "é"); }
while (testo.Contains("ì")) { testo = testo.Replace("ì", "ì"); }
while (testo.Contains("ò")) { testo = testo.Replace("ò", "ò"); }
while (testo.Contains("ù")) { testo = testo.Replace("ù", "ù"); }
while (testo.Contains("’")) { testo = testo.Replace("’", "'"); }
// Correggo scritto:
testo = testo.Replace("&#038;egrave", "è");
testo = testo.Replace("&amp;", "&");
testo = testo.Replace("&hellip;", "…");
testo = testo.Replace("\r", "");
testo = testo.Replace("\r", "\n");
while (testo.Contains("\t")) { testo = testo.Replace("\t", " "); }
while (testo.Contains("...")) { testo = testo.Replace("...", "…"); }
while (testo.Contains("….")) { testo = testo.Replace("….", "…"); }
@@ -631,8 +619,6 @@ namespace A69WD {
public static bool WriteStyleSheet(string FullFileName) {
try {
StringBuilder sb = new StringBuilder();
@@ -647,6 +633,102 @@ namespace A69WD {
public static void Insert_Format_Tags(ref List<string> dst, List<string> items) {
try {
// // Rimuove duplicati e ordina alfabeticamente
// items = items
// .OrderBy(s => s) // ordina alfabeticamente
// .Distinct() // elimina duplicati
// .ToList();
if (dst is null) { dst = new List<string>(); }
// Ordino ed elimino duplicati
items.Sort();
string tmp = "";
foreach (string item in items) {
string it = item.Trim().ToLower();
if (tmp != it) {
#region switch per formattare il testo
switch (it) {
case "altro": // Src: Old_Milu
dst.Add("Altro");
break;
case "autoerotismo": // Src: Old_Milu
dst.Add("Masturbazione");
break;
case "bdsm": // Src: A69
dst.Add("BDSM");
break;
case "cuckold": // Src: Old_Milu
dst.Add("Cuckold");
break;
case "dominazione": // Src: Old_Milu
dst.Add("Dominazione");
break;
case "gay": // Src: Old_Milu
dst.Add("Gay");
break;
case "gay & bisex": // Src: A69
dst.Add("Bisex");
dst.Add("Gay");
break;
case "incesto": // Src: A69, Old_Milu
dst.Add("Incesto");
break;
case "lesbo": // Src: A69
case "saffico": // Src: Old_Milu
dst.Add("Lesbo");
break;
case "lirica": // Src: Old_Milu
dst.Add("Lirica");
break;
case "lui & lei": // Src: A69
case "etero": // Src: Old_Milu
dst.Add("Etero");
break;
case "orge": // Src: A69
case "orgia": // Src: Old_Milu
dst.Add("Orge");
break;
case "poesia": // Src: Old_Milu
dst.Add("Poesia");
break;
case "prime esperienze": // Src: A69
dst.Add("Prime Esperienze");
break;
case "scambio di coppia": // Src: A69
dst.Add("Scambismo");
break;
case "sensazioni": // Src: Old_Milu
dst.Add("Sensazioni");
break;
case "tradimenti": // Src: A69
dst.Add("Tradimenti");
break;
case "trans": // Src: A69, Old_Milu
dst.Add("Trans");
break;
case "trio": // Src: A69, Old_Milu
dst.Add("trio");
break;
//case "":
// dst.Add("");
// break;
default:
dst.Add(item);
break;
}
#endregion
tmp = it;
}//if
}//foreach
} catch (Exception ex) {
Logger.Error(string.Format("Error Code: {0} - Errore in {1}.{2}: {3}", "ER00040", System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.FullName, System.Reflection.MethodInfo.GetCurrentMethod().Name, ex.Message));
} finally {
if (dst.Count == 0) dst.Add("_None_");
}
}//function
} // class Tools
+506 -266
View File
@@ -3,6 +3,8 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.Remoting.Messaging;
using System.Security.Policy;
using System.Text;
@@ -43,7 +45,7 @@ namespace A69WD.Net {
return !App.DB.IsPresent(URL);
}
}
}
}
public eLinkState Status { get => status; set { status = value; } }
@@ -85,7 +87,7 @@ namespace A69WD.Net {
return false;
}
}
public static bool InsertStartLink(Service_Crawler service) {
//string url = LinkIdxBase.Replace("{Categoria}", "altro").Replace("{ID}", "1"); //LinkIdxBase.Replace("{Categoria}", "trio").Replace("{ID}", "1");
@@ -174,7 +176,7 @@ namespace A69WD.Net {
rac.Title = cTools.FormattaTesto(nodes[0].InnerText.Trim(), false);
if (string.IsNullOrEmpty(rac.Title)) {
Logger.Error($"A69 - Import - No Title - {filePathName}");
return null;
return null;
}
} catch (Exception ex) { Logger.Error(string.Format("Fallita estrazione titolo dal file {0}\r\n{1}", FileName, ex.Message)); return null; }
#endregion
@@ -222,10 +224,11 @@ namespace A69WD.Net {
if (parts.Length == 3) {
CultureInfo culture = CultureInfo.CreateSpecificCulture("it-IT");
DateTimeStyles styles = DateTimeStyles.None;
rac.Creato = DateTime.Now;
if (!DateTime.TryParse(parts[0].Trim(), culture, styles, out rac.Creato)) {
DateTime tmpDT = DateTime.Now;
if (!DateTime.TryParse(parts[0].Trim(), culture, styles, out tmpDT)) {
Logger.Error($"A69 - Import - No data - {filePathName}");
}
rac.Creato = tmpDT;
}
}
} catch (Exception ex) { Logger.Warning(string.Format("Data non riconosciuta nel file {0}\r\n{1}", FileName, ex.Message)); return null; }
@@ -247,7 +250,9 @@ namespace A69WD.Net {
.OrderBy(s => s) // ordina alfabeticamente
.Distinct() // elimina duplicati
.ToList();
Format_Insert_Tags(rac, items); // rac.Tags = items;
List<string> tmpTags = new List<string>();
cTools.Insert_Format_Tags(ref tmpTags, items);
rac.Tags = tmpTags;
} catch (Exception ex) { Logger.Warning(string.Format("Fallita estrazione Tags dal file {0}\r\n{1}", FileName, ex.Message)); return null; }
#endregion
@@ -286,275 +291,510 @@ namespace A69WD.Net {
return rac;
}
protected virtual void Format_Insert_Tags(Data.cRac rac, List<string> items) {
// Per ora riporto tutti i tag rilevati, mentre in futuro dovrò:
// - parsare la lista e formattare correttamente i tag
// - ordinare la lista dei tag formattati ed eliminare i duplicati
// - se non sono rimasti tag, inserisco il tag generale per indicare che non vi sono tag
// - altrimenti inserisco tutti i tag rimasti.
rac.Tags = new List<string>();
public cRac EstraiRac(HtmlDocument doc) {
/*
* Analisi html dei racconti aggiornata al 2025/12/27:
*
*
* Per ciascun file html, si ha un unico racconto che ha un formato interno pari a:
* <html xmlns="http://www.w3.org/1999/xhtml">
* <body>
* ...
* <div class="targhetta_racconti" style=" width:100%; background-color:#5d5d5d;">incesto</div>
* ...
* <h1 class="titolone_nero" ...>Mia Madre , La mia Amante</h1>
* <strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;di<a href="/user/Bambino">Bambino</a>
* ...
* <div style="float:left; margin-right:5px; color: #9999CC; ">
* <span class="text_grigio" style="line-height:1.6em; color: #9999CC; font-size:13px;">&nbsp;<strong>Scritto il</strong> 22.08.2015 &nbsp;&nbsp; | &nbsp;&nbsp;
* <strong>Letture:</strong> 8.947&nbsp;&nbsp;| &nbsp;&nbsp;
* <strong>Votazione 8.0:</strong></span>
* </div>
* ...
* <div class="testoracc" style="color:#666666; text-align:justify; padding:20px 20px 0px 0px;">
* Eliminare i DIV interni che hanno cose grafiche
* Il testo è quello che rimane.
* </div>
* </body>
* </html>
*
* quindi i dati del racconto che si possono estrapolare sono:
* Titolo: --> Testo del nodo <h1 class="titolone_nero">
* SubTitle: --> Sempre Vuoto
* Autore: --> Testo del nodo <strong><a>
* Creato: --> Estrarre la parte dal <div style="float:left; margin-right:5px; color: #9999CC; ">
* Tags: --> Testo del nodo div class="targhetta_racconti">
* Trama: --> Testo del nodo <div class="testoracc"> a cui si levano i DIV interni.
* Notes: --> SrcFile - Nome del file che si analizza
* Origini: --> Sempre l'ID della sorgente web
* Voto: --> Sempre "Non Letto"
* Updated: --> Data attuale
*
*/
cRac rac = new cRac();
HtmlNodeCollection nodes;
try {
items.Sort();
string tmp = "";
foreach (string item in items) {
string it = item.Trim().ToLower();
if (tmp != it) {
#region switch per formattare il testo
switch (it) {
//case "racconti 69":
// rac.Tags.Add("69");
// break;
//case "abusi":
// rac.Tags.Add("Abusi");
// break;
//case "camgirl":
//case "chat e siti di incontri…":
//case "fiche":
// rac.Tags.Add("Altro");
// break;
//case "amatoriali":
// rac.Tags.Add("Amatoriali");
// break;
//case "anale":
// rac.Tags.Add("Anale");
// break;
//case "audio libri erotici":
//case "audio racconti erotici":
// rac.Tags.Add("Audio");
// break;
//case "bdsm":
// rac.Tags.Add("BDMS");
// break;
//case "bisex":
//case "bisex ♀":
//case "bisex ♂":
// rac.Tags.Add("Bisex");
// break;
//case "bondage":
// rac.Tags.Add("Bondage");
// break;
//case "bondage e sadomaso":
// rac.Tags.Add("Bondage");
// rac.Tags.Add("Sadomaso");
// break;
//case "bukkake":
// rac.Tags.Add("Bukkake");
// break;
//case "comici":
//case "comunità":
// rac.Tags.Add("Comico");
// break;
//case "concorso estate 2019":
//case "concorso natale 2018":
//case "decameron 2020":
// rac.Tags.Add("Concorso");
// break;
//case "confessioni":
// rac.Tags.Add("Confessioni");
// break;
//case "coppie":
//case "scambio di coppia":
//case "scambio di coppie":
// rac.Tags.Add("Coppie");
// break;
//case "cuckold":
//case "racconti cuckold":
// rac.Tags.Add("Cuckold");
// break;
//case "curiosità":
//case "curiosità sul sesso":
// rac.Tags.Add("Curiosità");
// break;
//case "dominazione":
//case "racconti di dominazione":
//case "schiavi&amp;padroni":
// rac.Tags.Add("Dominazione");
// break;
//case "doppia penetrazione":
// rac.Tags.Add("Doppia Penetrazione");
// break;
//case "esibizionismo":
// rac.Tags.Add("Esibizionismo");
// break;
//case "etero":
//case "lui & lei":
//case "racconti erotici etero":
// rac.Tags.Add("Etero");
// break;
//case "feticismo":
// rac.Tags.Add("Feticismo");
// break;
//case "fisting":
// rac.Tags.Add("Fisting");
// break;
//case "gang bang":
// rac.Tags.Add("Gang Bang");
// break;
//case "gay":
//case "racconti gay":
// rac.Tags.Add("Gay");
// break;
//case "gay & bisex":
//case "gay e bisex":
// rac.Tags.Add("Gay");
// rac.Tags.Add("Bisex");
// break;
//case "gay - travestiti":
// rac.Tags.Add("Gay");
// rac.Tags.Add("Trans");
// break;
//case "incesti":
//case "incesto":
//case "racconti erotici sull'incesto":
// rac.Tags.Add("Incesto");
// break;
//case "interviste":
//case "interviste erotiche":
// rac.Tags.Add("Intervista");
// break;
//case "lesbo":
//case "racconti erotici lesbo":
//case "saffico":
// rac.Tags.Add("Lesbo");
// break;
//case "autoerotismo":
//case "masturbazione":
//case "masturbazioni":
//case "racconti sull'autoerotismo":
// rac.Tags.Add("Masturbazione");
// break;
//case "milf":
// rac.Tags.Add("MILF");
// break;
//case "nudismo":
// rac.Tags.Add("Nudismo");
// break;
//case "orge":
//case "orgia":
//case "sesso di gruppo":
// rac.Tags.Add("Orge");
// break;
//case "pissing":
// rac.Tags.Add("Pissing");
// break;
//case "lirica erotica":
//case "poesia erotica":
//case "poesie":
//case "poesie erotiche":
// rac.Tags.Add("Poesia");
// break;
//case "pompini":
// rac.Tags.Add("Pompini");
// break;
//case "giochi erotici":
//case "prime esperienze":
//case "prime volte":
// rac.Tags.Add("Prime Esperienze");
// break;
//case "pulp":
// rac.Tags.Add("Pulp");
// break;
//case "sadomaso":
// rac.Tags.Add("Sadomaso");
// break;
//case "sensazioni":
//case "sentimentali":
// rac.Tags.Add("Sentimentali");
// break;
//case "cornuti":
//case "tradimenti":
//case "tradimento":
// rac.Tags.Add("Tradimento");
// break;
//case "racconti con trans":
//case "racconti trans":
//case "trans":
//case "trans e travestiti":
// rac.Tags.Add("Trans");
// break;
//case "trasgressivo":
// rac.Tags.Add("Trasgressivo");
// break;
//case "trio":
// rac.Tags.Add("Trio");
// break;
//case "uncategorized":
// rac.Tags.Add("Uncategorized");
// break;
//case "voyeur":
//case "voyeurismo":
// rac.Tags.Add("Voyeur");
// break;
//case "animali":
//case "zoofilia":
// rac.Tags.Add("Zoofilia");
// break;
Console.WriteLine($"A69 - Importo: {FileName}");
//case "Gay":
// rac.Tags.Add("Gay");
// break;
//case "Racconti bisex":
// rac.Tags.Add("Bisex");
// break;
//case "Racconti etero":
// rac.Tags.Add("Etero");
// break;
//case "Racconti fetish":
// rac.Tags.Add("Feticismo");
// break;
//case "Racconti incesto":
// rac.Tags.Add("Incesto");
// break;
//case "Racconti lesbo":
// rac.Tags.Add("Lesbo");
// break;
//case "Racconti masturbazione":
// rac.Tags.Add("Masturbazione");
// break;
//case "Racconti mature":
// rac.Tags.Add("Mature");
// break;
//case "Racconti orge":
// rac.Tags.Add("Orge");
// break;
//case "Racconti prima volta":
// rac.Tags.Add("Prime Esperienze");
// break;
//case "Racconti ricatto":
// rac.Tags.Add("Ricatto");
// break;
//case "Racconti scambio di coppia":
// rac.Tags.Add("Coppie");
// break;
//case "Racconti sottomissione":
// rac.Tags.Add("Dominazione");
// break;
//case "Racconti tradimenti":
// rac.Tags.Add("Tradimento");
// break;
//case "Racconti trav":
//case "Trans":
// rac.Tags.Add("Trans");
// break;
// Ricavo il nodo radice del racconto:
HtmlNode root = doc.DocumentNode;
if (root == null) {
// racconto non presente
return null;
}
default:
rac.Tags.Add(item);
break;
#region Origini: --> Sempre l'ID della sorgente web
rac.Origini = new List<string> { ID };
#endregion
#region Titolo: --> Testo del nodo <h1 class="titolone_nero">
try {
nodes = root.SelectNodes("//h1[@class='titolone_nero']");
rac.Title = cTools.FormattaTesto(nodes[0].InnerText.Trim(), false);
if (string.IsNullOrEmpty(rac.Title)) {
Logger.Error($"A69 - Import - No Title - {FileName}");
return null;
}
} catch (Exception ex) { Logger.Error(string.Format("Fallita estrazione titolo dal file {0}\r\n{1}", FileName, ex.Message)); return null; }
#endregion
#region SubTitle: --> Sempre Vuoto
rac.SubTitle = "";
#endregion
#region Autore: --> Testo del nodo <strong><a>
try {
// Autore --> Testo del nodo <span class="text_grigio><a>"
nodes = root.SelectNodes("//span[@class='text_grigio']//a");
List<string> items = new List<string>();
foreach (HtmlNode nd in nodes) {
string aut = nd.ParentNode.InnerText.Replace("&nbsp;", "").Trim();
if (aut.StartsWith("di")) {
items.Add(aut.Replace("di", "").Trim());
}
#endregion
tmp = it;
}
items.Sort();
string tmp = "";
rac.Autori = new List<string>();
// Rimuovo eventuali duplicati
foreach (string aut in items) {
if (tmp != aut) {
rac.Autori.Add(aut);
tmp = aut;
}
}
if (rac.Autori == null || string.IsNullOrEmpty(rac.Autori[0])) {
Logger.Error($"A69 - Import - No Autors - {FileName}");
return null;
}
} catch (Exception ex) { Logger.Error(string.Format("Fallita estrazione autore dal file {0}\r\n{1}", FileName, ex.Message)); return null; }
#endregion
}//if
#region Creato: --> Estrarre la parte dal <div style="float:left; margin-right:5px; color: #9999CC; ">
rac.Creato = DateTime.Now;
try {
// Creato --> Estrarre la prima parte dal <span class="text_grigio"> che ha il testo "Data|Visite|Commenti":
nodes = root.SelectNodes("//span[@class='text_grigio']");
foreach (HtmlNode nd in nodes) {
string txt = nd.InnerText.Trim().Replace("&nbsp;", "");
string[] parts = txt.Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 3) {
CultureInfo culture = CultureInfo.CreateSpecificCulture("it-IT");
DateTimeStyles styles = DateTimeStyles.None;
DateTime tmpDT = DateTime.Now;
if (!DateTime.TryParse(parts[0].Trim(), culture, styles, out tmpDT)) {
Logger.Error($"A69 - Import - No data - {FileName}");
}
rac.Creato = tmpDT;
}
}
} catch (Exception ex) { Logger.Warning(string.Format("Data non riconosciuta nel file {0}\r\n{1}", FileName, ex.Message)); return null; }
#endregion
}//foreach
#region Updated: --> Data attuale
rac.Updated = DateTime.Now;
#endregion
#region Tags: --> Testo del nodo div class="targhetta_racconti">
try {
nodes = root.SelectNodes("//div[@class='targhetta_racconti']");
List<string> items = new List<string>();
foreach (HtmlNode nd in nodes) {
items.Add(cTools.FormattaTesto(nd.InnerText.Replace("genere", "").Trim(), false));
}
//items.Sort();
items = items
.OrderBy(s => s) // ordina alfabeticamente
.Distinct() // elimina duplicati
.ToList();
//Format_Insert_Tags(rac, items); // rac.Tags = items;
List<string> tmpTags = new List<string>();
cTools.Insert_Format_Tags(ref tmpTags, items);
rac.Tags = tmpTags;
} catch (Exception ex) { Logger.Warning(string.Format("Fallita estrazione Tags dal file {0}\r\n{1}", FileName, ex.Message)); return null; }
#endregion
#region Trama: --> Testo del nodo <div class="testoracc"> a cui si levano i DIV interni.
try {
nodes = root.SelectNodes("//div[@class='testoracc']");
foreach (HtmlNode nd in nodes) {
foreach (HtmlNode n in nd.ChildNodes) {
if (n.Name.ToUpper() == "#TEXT") { rac.Trama += cTools.FormattaTesto(n.InnerText.Trim(), false); }
if (n.Name.ToUpper() == "BR") { rac.Trama += "\r"; }
if (n.Name.ToUpper() == "DIV") {; }
}
}
} catch (Exception ex) { Logger.Error(string.Format("Fallita estrazione Trama dal file {0}\r\n{1}", FileName, ex.Message)); return null; }
#endregion
#region Voto: --> Sempre "Non Letto"
rac.Voto = "Non Letto";
#endregion
#region Notes: --> SrcFile - Nome del file che si analizza
rac.Notes = new List<Data.cNota>();
rac.Notes.Add(new Data.cNota("SrcFile", System.IO.Path.GetFileName(FileName)));
#endregion
// Formalizzo esito estrazione:
if (!rac.Validate()) {
rac = null;
}
} catch (Exception ex) {
Logger.Error(string.Format("Error Code: {0} - Errore in {1}.{2}: {3}", "ER00040", System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.FullName, System.Reflection.MethodInfo.GetCurrentMethod().Name, ex.Message));
} finally {
if (rac.Tags.Count == 0) rac.Tags.Add("_None_");
Logger.Error($"A69 - Import Error - {FileName}: {ex.Message}");
Console.WriteLine($"A69 - Import Error - {FileName}: {ex.Message}");
return null;
}
}//function
return rac;
}
#region Gestione Sito A69
private static HttpClient client = null;
private static readonly string nickname = @"Toky78";
private static readonly string password = @"p!fw05Q9Hc6j5";
private static bool autenticated = false;
/// <summary>
/// Indica se siamo autenticati al sito
/// </summary>
public static bool Autenticated => autenticated;
/// <summary>
/// Esegue l'autenticazione al sito:
/// - Scarica la pagina di autenticazione
/// - analizza quanto scaricato per determinare i campi dinamici 'ts' e 'token'
/// - prepara la risposta al form di identificazione
/// - invia il POST di autenticazione
/// - analizza la risposta per verificare il corretto login
/// </summary>
public static async Task<bool> Authenticate() {
if (autenticated) {
// Autenticazione già fatta, posso proseguire senza altro fare
Logger.Warning("A69 - Autenticazione già effettuata.");
}
try {
#region 1) Prepare HTML Client
HttpClientHandler _handler = new HttpClientHandler {
CookieContainer = new CookieContainer(),
UseCookies = true,
AllowAutoRedirect = true,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
client = new HttpClient(_handler);
client.Timeout = TimeSpan.FromSeconds(60);
client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
client.DefaultRequestHeaders.Referrer = new Uri(cLink_A69.LoginPageUrl);
#endregion
#region 2) GET pagina di login
Console.WriteLine("A69 - Richiedo la pagina di login...");
Logger.Information("A69 - Richiedo la pagina di login...");
string loginPageHtml = await client.GetStringAsync(cLink_A69.LoginPageUrl);
#endregion
#region 3) Parsing HTML per estrarre ts e token
var doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(loginPageHtml);
string ts = doc.DocumentNode
.SelectSingleNode("//input[@name='ts']")
?.GetAttributeValue("value", "");
string token = doc.DocumentNode
.SelectSingleNode("//input[@name='token']")
?.GetAttributeValue("value", "");
if (string.IsNullOrWhiteSpace(ts) || string.IsNullOrWhiteSpace(token)) {
Console.WriteLine("A69 - Impossibile estrarre 'ts' o 'token' dalla pagina di login.");
Logger.Information("A69 - Impossibile estrarre 'ts' o 'token' dalla pagina di login.");
return autenticated;
}
Console.WriteLine($"A69 - Parametri dinamici trovati: ts={ts}, token={token}");
Logger.Information($"A69 - Parametri dinamici trovati: ts={ts}, token={token}");
#endregion
#region 4) POST login con valori dinamici
var loginData = new FormUrlEncodedContent(new[] {
new KeyValuePair<string,string>("nick", nickname),
new KeyValuePair<string,string>("pwd", password),
new KeyValuePair<string,string>("ricordami","1"),
new KeyValuePair<string,string>("login", "Entra"),
new KeyValuePair<string,string>("ts", ts),
new KeyValuePair<string,string>("token", token)
});
Console.WriteLine("Invio credenziali di login...");
var loginResponse = await client.PostAsync(cLink_A69.LoginPostUrl, loginData);
if (!loginResponse.IsSuccessStatusCode) {
Console.WriteLine($"A69 - Login fallito. HTTP {(int)loginResponse.StatusCode} {loginResponse.ReasonPhrase}");
Logger.Information($"A69 - Login fallito. HTTP {(int)loginResponse.StatusCode} {loginResponse.ReasonPhrase}");
return autenticated;
}
#endregion
#region 5) Verifica autenticazione correttamente avvenuta
// Verifica semplice: sulla pagina di login effettuato, ricerco il nome dell'utente:
string loginResult = await loginResponse.Content.ReadAsStringAsync();
if (loginResult.Contains(nickname)) {
Console.WriteLine("A69 - Login effettuato con successo! Sessione autenticata attiva.");
Logger.Information("A69 - Login effettuato con successo! Sessione autenticata attiva.");
autenticated = true;
} else {
Console.WriteLine("A69 - Login non riuscito: il server ha restituito un messaggio di errore.");
Logger.Information("A69 - Login non riuscito: il server ha restituito un messaggio di errore.");
autenticated = false;
}
#endregion
} catch (Exception ex) {
Console.WriteLine($"Errore generale: {ex.Message}");
Logger.Information($"Errore generale: {ex.Message}");
}
return autenticated;
}
/// <summary>
/// Codice da eseguire per ciascun A69 link:
/// - scarico l'url indicato
/// - se richiesto lo salvo come file di testo
/// - analizzo il documento html ed estraggo tutti gli url ad altre pagine di link
/// - analizzo il documento html ed estraggo tutti gli url a racconti
/// - analizzo il documento html ed estraggo il testo del racconto
/// </summary>
/// <param name="lk"></param>
/// <returns></returns>
public async Task<List<iLink>> CrawlPageAsync() {
var newlinks = new List<iLink>();
try {
Console.WriteLine($"A69 - Scarico: {URL}");
HtmlDocument tdoc = null;
string thtml = "";
#region ToConfirm
if (Status == eLinkState.ToConfirm) {
// Eseguo la verifica se link da downloadare
Status = eLinkState.ToDownload;
}
#endregion
#region ToDownload
if (Status == eLinkState.ToDownload) {
Status = eLinkState.OnDownload;
}
#endregion
#region OnDownload
if (Status == eLinkState.OnDownload) {
// Scarico il file
var resp = await client.GetAsync(URL);
if (!resp.IsSuccessStatusCode) {
Console.WriteLine($"A69 - Errore HTTP {(int)resp.StatusCode} {resp.ReasonPhrase} per {URL}");
Status = eLinkState.Error;
//App.DB.LinkLog(lk);
return newlinks;
}
// Scarico la pagina
thtml = await client.GetStringAsync(URL);
tdoc = new HtmlDocument();
tdoc.LoadHtml(thtml);
// Salvo il file downloadato
//SaveToFile(thtml, lk.FilePath, lk.FileName);
Status = eLinkState.ToAnalize;
}
#endregion
#region ToAnalize
{
if (Status == eLinkState.ToAnalize) {
List<string> tmpLk = new List<string>();
#region Estraggo gli url ad indici:
{
/*
* Gli url delle pagine di indice sono del tipo:
*
* <div id="pagination">
* <ul>
* <li class="prev"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=2">&lsaquo;</a></li>
* <li class="page"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=1">1</a></li>
* <li class="page"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=2">2</a></li>
* <li class="currentpage">3</li>
* <li class="page"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=4">4</a></li>
* <li class="page"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=5">5</a></li>
* <li class="next"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=4">&rsaquo;</a></li>
* </ul>
* </div>
*
*/
try {
// Estraggo i link alle altre pagine:
var tnodes = tdoc.DocumentNode.SelectNodes("//div[@id='pagination']//ul//li//a[@href]");
if (tnodes != null) {
foreach (var link in tnodes) {
var href = link.GetAttributeValue("href", "");
if (!string.IsNullOrEmpty(href) && href.StartsWith("/racconti-erotici/racconti_lista.php?genere="))
tmpLk.Add(href); //tmpLk.Add(cLink_A69.PageBase + href);
}
}
// Rimuove duplicati e ordina alfabeticamente
tmpLk = tmpLk
.OrderBy(s => s) // ordina alfabeticamente
.Distinct() // elimina duplicati
.ToList();
// Inserisco i link trovati
foreach (string ilk in tmpLk) {
string[] separator = new string[] { "&pagina=" };
string[] txt = ilk.Split(separator, StringSplitOptions.RemoveEmptyEntries);
string url = cLink_A69.PageBase + ilk;
string name = txt[0].Replace("/racconti-erotici/racconti_lista.php?genere=", "_A69_") + "_" + int.Parse(txt[1]).ToString("0000") + ".html";
newlinks.Add(new cLink_A69(url, name));
}
} catch (Exception ex) {
Logger.Error("Errore analisi Idx link dal " + FileName + " - " + ex.Message);
cTools.SaveToFile(thtml, Cfg.Settings.PathSrcErrOnExtract, FileName);
}
}
#endregion
#region Estraggo gli url a racconti:
{
/*
*
* I link ai racconti sono dei pulsanti così formati:
* <div class="continua">
* <a href="/racconti-erotici/altro/Tutto-e-iniziato-per-gioco-pazza-famiglia_166770.html">Continua a leggere » </a>
* </div>
*/
try {
// Estraggo i link ai racconti:
tmpLk = new List<string>();
// Estraggo i link alle altre pagine:
var tnodes = tdoc.DocumentNode.SelectNodes("//div[@class='continua']//a[@href]");
if (tnodes != null) {
foreach (var link in tnodes) {
var href = link.GetAttributeValue("href", "");
if (!string.IsNullOrEmpty(href) && href.StartsWith("/racconti-erotici/") && (href.EndsWith(".html") || href.EndsWith(".htm")))
tmpLk.Add(href); //tmpLk.Add(cLink_A69.PageBase + href);
}
}
// Rimuove duplicati e ordina alfabeticamente
tmpLk = tmpLk
.OrderBy(s => s) // ordina alfabeticamente
.Distinct() // elimina duplicati
.ToList();
// Inserisco i link trovati
foreach (string ilk in tmpLk) {
string url = cLink_A69.PageBase + ilk;
string name = ilk.Replace("/racconti-erotici/", "A69_").Replace(@"/", "_");
newlinks.Add(new cLink_A69(url, name));
}
} catch (Exception ex) {
Logger.Error("Errore analisi Idx link dal " + FileName + " - " + ex.Message);
cTools.SaveToFile(thtml, Cfg.Settings.PathSrcErrOnExtract, FileName);
}
}
#endregion
#region Estraggo il racconto dal file e lo inserisco nel DB
if (FileName.StartsWith("A69_")) {
// Link ad un racconto
cRac rac = EstraiRac(tdoc);
if (rac != null) {
// Racconto estratto correttamente, lo salvo sul DB
int res = App.DB.ImportRac(rac);
if (res > 0) {
// Racconto salvato correttamente sul DB
cTools.SaveToFile(thtml, Cfg.Settings.PathSrcDone, FileName);
} else {
// Errore nel salvataggio sul DB
cTools.SaveToFile(thtml, Cfg.Settings.PathSrcErrOnSave, FileName);
}
} else {
// Estrazione del racconto fallita
cTools.SaveToFile(thtml, Cfg.Settings.PathSrcErrOnExtract, FileName);
}
} else {
// Link ad un indice
cTools.SaveToFile(thtml, Cfg.Settings.PathIdxDone, FileName);
}
#endregion
Status = eLinkState.End;
App.DB.LinkLog(this);
}
}
#endregion
#region End
if (Status == eLinkState.End) {
}
#endregion
#region Error
if (Status == eLinkState.Error) {
}
#endregion
#region Abort
if (Status == eLinkState.Abort) {
}
#endregion
#region Cancel
if (Status == eLinkState.Cancel) {
}
#endregion
Console.WriteLine("Link.Count=" + newlinks.Count.ToString("00"));
} catch (Exception ex) {
Console.WriteLine($"Errore con {URL}: {ex.Message}");
Status = eLinkState.Error;
App.DB.LinkLog(this);
}
return newlinks;
}
#endregion
+589
View File
@@ -0,0 +1,589 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using A69WD.Data;
using A69WD.Service;
using HtmlAgilityPack;
using System.Globalization;
using System.Xml.Serialization;
namespace A69WD.Net {
public class cLink_eRac : iLink {
private readonly string filename;
private readonly string filepath;
private eLinkState status = eLinkState.ToConfirm;
private string fields = "";
public static string PageBase { get { return "https://www.eroticiracconti.it"; } }
public static string LinkIdxBase { get { return "https://www.eroticiracconti.it/archivio/Tutto/{ID}"; } }
public static string LinkRacBase { get { return "https://www.eroticiracconti.it/racconto/{RacID}"; } }
public string ID => "eRac";
public string URL { get; }
public string FileName => filename;
public string FilePath { get => filepath; }
public bool Validate {
get {
if (URL.StartsWith("https://www.eroticiracconti.it/archivio/Tutto/")) {
// URL di tipo Idx, devo lavorare i link nuovo o rilavorare i link più obsoleti di 6 ore.
return !App.DB.IsPresent(URL, -6);
} else {
// URL di tipo Rac, devo lavorare solo i link nuovi
return !App.DB.IsPresent(URL);
}
}
}
public eLinkState Status { get => status; set { status = value; } }
public string Fields { get => fields; set => fields = value; }
public cLink_eRac(string Url, string Filename, string fields = "none", string Filepath = "") {
this.URL = Url;
this.filename = Filename;
this.fields = fields;
this.filepath = string.IsNullOrEmpty(Filepath) ? Cfg.Settings.PathDownloadWorking : Filepath;
this.status = eLinkState.ToConfirm;
}
public static bool InsertStartLink(Service_Crawler service) {
try {
int i = 0;
string url = LinkIdxBase.Replace("{ID}", i.ToString());
string name = $"_eRac_Idx_{i.ToString("0000")}.html";
service.EnqueueUrl(new cLink_eRac(url, name));
return true;
} catch (Exception ex) {
Logger.Error("Errore inserendo eRac Start Link - " + ex.Message);
return false;
}
}
public cRac EstraiRac(HtmlDocument doc) {
/*
* Analisi html dei racconti aggiornata al 2025/12/27:
*
*
* Per ciascun file html, si ha un unico racconto che ha un formato interno pari a:
* <!DOCTYPE html>
* <html lang="it">
* <head>
* ...
* <title> Quante voglie. | Erotici Racconti</title>
* </head>
* <body>
* <div class="main">
* <main>
* <div class="story">
* <h1 class="tit">{Title}</h1>
* <h2>
* <span class="infos">
* <span>
* <svg class="icon"><use xlink:href="#icon-autore"></use></svg>
* di<br>
* <a href="{URL_Autor}" rel="author">{Autor}</a>
* </span>
* <span>
* <svg class="icon"><use xlink:href="#icon-cat-etero"></use></svg>
* genere<br>
* <a href="{URL_Genere}">{Genere}</a>
* </span>
* </span>
* </h2>
* <div class="tes">
* {Trama}
* </div>
* <div class="infos">
* <div>
* <svg class="icon"><use xlink:href="#icon-autore"></use></svg>
* di<br><strong>
* <a href="{URL_Autor}" rel="author">{Autor}</a>
* </div>
* <div>
* <svg class="icon"><use xlink:href="#icon-calendario"></use></svg>
* scritto il<br>
* <strong>{Data}</strong>
* </div>
* <div>
* <svg class="icon"><use xlink:href="#icon-eye"></use></svg>
* <span class="counterBoard"><span class="digit d20">5</span><span class="digit d20">.</span><span class="digit d20">1</span><span class="digit d20">K</span></span>
* <br>
* visite
* </div>
* </div>
* <div class="infos">
* <div>
* <svg class="icon"><use xlink:href="#icon-vote-check"></use></svg>
* <span class="counterBoard">
* <span class="digit d20">3</span>
* </span><br>
* voti
* </div>
* <div>
* <svg class="icon"><use xlink:href="#icon-medaglia"></use></svg>
* valutazione<br>
* <strong>2</strong><br>
* <div class="scoreBar"><div class="bar"><div class="inner" style="width: 100%"></div></div><div class="bar"><div class="inner" style="width: 100%"></div></div><div class="bar"><div class="inner" style="width: 0%"></div></div><div class="bar"><div class="inner" style="width: 0%"></div></div><div class="bar"><div class="inner" style="width: 0%"></div></div><div class="bar"><div class="inner" style="width: 0%"></div></div><div class="bar"><div class="inner" style="width: 0%"></div></div><div class="bar"><div class="inner" style="width: 0%"></div></div><div class="bar"><div class="inner" style="width: 0%"></div></div><div class="bar"><div class="inner" style="width: 0%"></div></div></div>
* </div>
* </div>
*
* <div class="infos">
* <div>
* <svg class="icon"><use xlink:href="#icon-ballot-box"></use></svg>
* il tuo voto<br>
* <div class="selector ">
* <a href="#" score="0.1" class="sec ">1</a><a href="#" score="0.2" class="sec ">2</a><a href="#" score="0.3" class="sec ">3</a><a href="#" score="0.4" class="sec ">4</a><a href="#" score="0.5" class="sec ">5</a><a href="#" score="0.6" class="sec ">6</a><a href="#" score="0.7" class="sec ">7</a><a href="#" score="0.8" class="sec ">8</a><a href="#" score="0.9" class="sec ">9</a><a href="#" score="1" class="sec ">10</a> <div class="loadingBar"></div>
* </div>
* </div>
* </div>
* ...
* </div>
* ...
* </main>
* ...
* </div>
* </body>
* </html>
*
* quindi i dati del racconto che si possono estrapolare sono:
* Titolo: --> Testo del nodo <div class="story"><h1 class="tit">{Title}</h1></div>
* SubTitle: --> Sempre Vuoto
* Autore: --> Testo del nodo <div class="story"><h2><span class="infos"><span><a href="{URL_Autor}" rel="author">{Autor}</a></span></span></h2></div>
* Creato: --> Estrarre la parte dal <div class="story"><div class="infos"><div><strong>{Data}</strong></div></div></div>
* Tags: --> Testo del nodo <div class="story"><h2><span class="infos"><span><a href="{URL_Genere}">{Genere}</a></span></span></h2></div>
* Trama: --> Testo del nodo <div class="story"><div class="tes">{Trama}</div></div>
* Notes: --> SrcFile - Nome del file che si analizza
* Origini: --> Sempre l'ID della sorgente web
* Voto: --> Sempre "Non Letto"
* Updated: --> Data attuale
*
*/
cRac rac = new cRac();
HtmlNodeCollection nodes;
try {
Console.WriteLine($"eRac - Importo: {FileName}");
// Ricavo il nodo radice del racconto: <div class="story">
//HtmlNode root = doc.DocumentNode;
HtmlNode root = doc.DocumentNode.SelectSingleNode("//div[@class='story']");
if (root == null) {
// racconto non presente
return null;
}
#region Origini: --> Sempre l'ID della sorgente web
rac.Origini = new List<string> { ID };
#endregion
#region Titolo: --> Testo del nodo <div class="story"><h1 class="tit">{Title}</h1></div>
try {
nodes = root.SelectNodes("//h1[@class='tit']");
rac.Title = cTools.FormattaTesto(nodes[0].InnerText.Trim(), false);
if (string.IsNullOrEmpty(rac.Title)) {
Logger.Error($"eRac - Import - No Title - {FileName}");
return null;
}
} catch (Exception ex) { Logger.Error(string.Format("Fallita estrazione titolo dal file {0}\r\n{1}", FileName, ex.Message)); return null; }
#endregion
#region SubTitle: --> Sempre Vuoto
rac.SubTitle = "";
#endregion
#region Autore: --> Testo del nodo <div class="story"><h2><span class="infos"><span><a href="{URL_Autor}" rel="author">{Autor}</a></span></span></h2></div>
try {
//nodes = root.SelectNodes("//h2//span[@class='infos']//span[]//a");
nodes = root.SelectNodes("//a[@rel='author']");
List<string> items = new List<string>();
foreach (HtmlNode nd in nodes) {
string aut = nd.ParentNode.InnerText.Replace("&nbsp;", "").Trim();
if (aut.StartsWith("di")) {
items.Add(aut.Replace("di", "").Trim());
}
}
items.Sort();
string tmp = "";
rac.Autori = new List<string>();
// Rimuovo eventuali duplicati
foreach (string aut in items) {
if (tmp != aut) {
rac.Autori.Add(aut);
tmp = aut;
}
}
if (rac.Autori == null || string.IsNullOrEmpty(rac.Autori[0])) {
Logger.Error($"eRac - Import - No Autors - {FileName}");
return null;
}
} catch (Exception ex) { Logger.Error(string.Format("Fallita estrazione autore dal file {0}\r\n{1}", FileName, ex.Message)); return null; }
#endregion
#region Creato: --> Estrarre la parte dal <div style="float:left; margin-right:5px; color: #9999CC; ">
rac.Creato = DateTime.Now;
try {
// Creato --> Estrarre la prima parte dal <div class="story"><div class="infos"><div><strong>{Data}</strong></div></div></div>
nodes = root.SelectNodes("//div[@class='infos']//div");
foreach (HtmlNode nd in nodes) {
string txt = nd.InnerText.Trim().Replace("&nbsp;", "");
if (txt.StartsWith("scritto il")) {
CultureInfo culture = CultureInfo.CreateSpecificCulture("it-IT");
DateTimeStyles styles = DateTimeStyles.None;
DateTime tmpDT = DateTime.Now;
if (!DateTime.TryParse(txt.Replace("scritto il", "").Trim(), culture, styles, out tmpDT)) {
Logger.Error($"eRac - Import - No data - {FileName}");
}
rac.Creato = tmpDT;
break;
}
}
} catch (Exception ex) { Logger.Warning(string.Format("Data non riconosciuta nel file {0}\r\n{1}", FileName, ex.Message)); return null; }
#endregion
#region Updated: --> Data attuale
rac.Updated = DateTime.Now;
#endregion
#region Tags: --> Testo del nodo <div class="story"><h2><span class="infos"><span><a href="{URL_Genere}">{Genere}</a></span></span></h2></div>
try {
//nodes = root.SelectNodes("//h2//span[@class='infos']//span//a");
nodes = root.SelectNodes("//h2//span[@class='infos']//span");
List<string> items = new List<string>();
foreach (HtmlNode nd in nodes) {
string txt = nd.InnerText.Trim();
if (txt.StartsWith("genere")) {
items.Add(cTools.FormattaTesto(txt.Replace("genere", "").Trim(), false));
}
}
//items.Sort();
items = items
.OrderBy(s => s) // ordina alfabeticamente
.Distinct() // elimina duplicati
.ToList();
//Format_Insert_Tags(rac, items); // rac.Tags = items;
List<string> tmpTags = new List<string>();
cTools.Insert_Format_Tags(ref tmpTags, items);
rac.Tags = tmpTags;
} catch (Exception ex) { Logger.Warning(string.Format("Fallita estrazione Tags dal file {0}\r\n{1}", FileName, ex.Message)); return null; }
#endregion
#region Trama: --> Testo del nodo <div class="testoracc"> a cui si levano i DIV interni.
try {
nodes = root.SelectNodes("//div[@class='tes']");
foreach (HtmlNode nd in nodes) {
foreach (HtmlNode n in nd.ChildNodes) {
if (n.Name.ToUpper() == "#TEXT") { rac.Trama += cTools.FormattaTesto(n.InnerText.Trim(), false); }
if (n.Name.ToUpper() == "BR") { rac.Trama += "\r"; }
if (n.Name.ToUpper() == "DIV") {; }
}
}
} catch (Exception ex) { Logger.Error(string.Format("Fallita estrazione Trama dal file {0}\r\n{1}", FileName, ex.Message)); return null; }
#endregion
#region Voto: --> Sempre "Non Letto"
rac.Voto = "Non Letto";
#endregion
#region Notes: --> SrcFile - Nome del file che si analizza
rac.Notes = new List<Data.cNota>();
rac.Notes.Add(new Data.cNota("SrcFile", System.IO.Path.GetFileName(FileName)));
#endregion
// Formalizzo esito estrazione:
if (!rac.Validate()) {
rac = null;
}
} catch (Exception ex) {
Logger.Error($"eRac - Import Error - {FileName}: {ex.Message}");
Console.WriteLine($"eRac - Import Error - {FileName}: {ex.Message}");
return null;
}
return rac;
}
#region Gestione Sito eRac
private static HttpClient client = null;
private static bool autenticated = false;
/// <summary>
/// Indica se siamo autenticati al sito
/// </summary>
public static bool Autenticated => autenticated;
/// <summary>
/// Esegue l'autenticazione al sito:
/// - Scarica la pagina di autenticazione
/// - analizza quanto scaricato per determinare i campi dinamici 'ts' e 'token'
/// - prepara la risposta al form di identificazione
/// - invia il POST di autenticazione
/// - analizza la risposta per verificare il corretto login
/// </summary>
public static async Task<bool> Authenticate() {
if (autenticated) {
// Autenticazione già fatta, posso proseguire senza altro fare
Logger.Warning("eRac - Autenticazione già effettuata.");
}
try {
#region 1) Prepare HTML Client
HttpClientHandler _handler = new HttpClientHandler {
CookieContainer = new CookieContainer(),
UseCookies = true,
AllowAutoRedirect = true,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
client = new HttpClient(_handler);
client.Timeout = TimeSpan.FromSeconds(60);
client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
client.DefaultRequestHeaders.Referrer = new Uri(cLink_eRac.PageBase);
#endregion
autenticated = true;
} catch (Exception ex) {
Console.WriteLine($"Errore generale: {ex.Message}");
Logger.Information($"Errore generale: {ex.Message}");
}
return autenticated;
}
/// <summary>
/// Codice da eseguire per ciascun eRac link:
/// - scarico l'url indicato
/// - se richiesto lo salvo come file di testo
/// - analizzo il documento html ed estraggo tutti gli url ad altre pagine di link
/// - analizzo il documento html ed estraggo tutti gli url a racconti
/// - analizzo il documento html ed estraggo il testo del racconto
/// </summary>
/// <param name="lk"></param>
/// <returns></returns>
public async Task<List<iLink>> CrawlPageAsync() {
var newlinks = new List<iLink>();
try {
Console.WriteLine($"eRac - Scarico: {URL}");
HtmlDocument tdoc = null;
string thtml = "";
#region ToConfirm
if (Status == eLinkState.ToConfirm) {
// Eseguo la verifica se link da downloadare
Status = eLinkState.ToDownload;
}
#endregion
#region ToDownload
if (Status == eLinkState.ToDownload) {
Status = eLinkState.OnDownload;
}
#endregion
#region OnDownload
if (Status == eLinkState.OnDownload) {
// Scarico il file
var resp = await client.GetAsync(URL);
if (!resp.IsSuccessStatusCode) {
Console.WriteLine($"eRac - Errore HTTP {(int)resp.StatusCode} {resp.ReasonPhrase} per {URL}");
Status = eLinkState.Error;
//App.DB.LinkLog(lk);
return newlinks;
}
// Scarico la pagina
thtml = await client.GetStringAsync(URL);
tdoc = new HtmlDocument();
tdoc.LoadHtml(thtml);
// Salvo il file downloadato
//SaveToFile(thtml, lk.FilePath, lk.FileName);
Status = eLinkState.ToAnalize;
}
#endregion
#region ToAnalize
{
if (Status == eLinkState.ToAnalize) {
List<string> tmpLk = new List<string>();
#region Estraggo gli url ad indici:
{
/*
* Gli url delle pagine di indice sono del tipo:
*
* <div class="pagination">
* <a href="/archivio/Tutto">⇤</a>
* <a rel="prev" href="/archivio/Tutto/0">⇠</a>
* <a href="/archivio/Tutto/0">1</a>
* <a class="current">2</a>
* <a href="/archivio/Tutto/2">3</a>
* <a href="/archivio/Tutto/3">4</a>
* <a href="/archivio/Tutto/4">5</a>
* <a href="/archivio/Tutto/5">6</a>
* <a href="/archivio/Tutto/6">7</a>
* <a href="/archivio/Tutto/7">8</a>
* <a href="/archivio/Tutto/8">9</a>
* <a href="/archivio/Tutto/9">10</a>
* <a href="/archivio/Tutto/10">11</a>
* <a class="plain">...</a>
* <a rel="next" href="/archivio/Tutto/2">⇢</a>
* <a href="/archivio/Tutto/3146">⇥</a>
* </div>
*
*/
try {
// Estraggo i link alle altre pagine:
var tnodes = tdoc.DocumentNode.SelectNodes("//div[@class='pagination']//a[@href]");
if (tnodes != null) {
foreach (var link in tnodes) {
var href = link.GetAttributeValue("href", "");
if (!string.IsNullOrEmpty(href) && href.StartsWith("/archivio/Tutto/"))
tmpLk.Add(href); //tmpLk.Add(cLink_eRac.PageBase + href);
}
}
// Rimuove duplicati e ordina alfabeticamente
tmpLk = tmpLk
.OrderBy(s => s) // ordina alfabeticamente
.Distinct() // elimina duplicati
.ToList();
// Inserisco i link trovati
foreach (string ilk in tmpLk) {
string url = cLink_eRac.PageBase + ilk;
int offset = int.Parse(ilk.Replace("/archivio/Tutto/", ""));
string name = $"_eRac_Idx_{offset.ToString("0000")}.html";
newlinks.Add(new cLink_eRac(url, name));
}
} catch (Exception ex) {
Logger.Error("Errore analisi Idx link dal " + FileName + " - " + ex.Message);
cTools.SaveToFile(thtml, Cfg.Settings.PathSrcErrOnExtract, FileName);
}
}
#endregion
#region Estraggo gli url a racconti:
{
/*
*
* I link ai racconti sono dei pulsanti così formati:
* <a class="rac" href="{URL}"></a>
*
*/
try {
// Estraggo i link ai racconti:
tmpLk = new List<string>();
// Estraggo i link alle altre pagine:
var tnodes = tdoc.DocumentNode.SelectNodes("//a[@class='rac']");
if (tnodes != null) {
foreach (var link in tnodes) {
var href = link.GetAttributeValue("href", "");
if (!string.IsNullOrEmpty(href) && href.StartsWith("/racconto/"))
tmpLk.Add(href); //tmpLk.Add(cLink_eRac.PageBase + href);
}
}
// Rimuove duplicati e ordina alfabeticamente
tmpLk = tmpLk
.OrderBy(s => s) // ordina alfabeticamente
.Distinct() // elimina duplicati
.ToList();
// Inserisco i link trovati
foreach (string ilk in tmpLk) {
string url = cLink_eRac.PageBase + ilk;
string name = ilk.Replace("/racconto/", "eRac_").Replace(@"/", "_") + ".html";
newlinks.Add(new cLink_eRac(url, name));
}
} catch (Exception ex) {
Logger.Error("Errore analisi Idx link dal " + FileName + " - " + ex.Message);
cTools.SaveToFile(thtml, Cfg.Settings.PathSrcErrOnExtract, FileName);
}
}
#endregion
#region Estraggo il racconto dal file e lo inserisco nel DB
if (FileName.StartsWith("eRac_")) {
// Link ad un racconto
cRac rac = EstraiRac(tdoc);
if (rac != null) {
// Racconto estratto correttamente, lo salvo sul DB
int res = App.DB.ImportRac(rac);
if (res > 0) {
// Racconto salvato correttamente sul DB
cTools.SaveToFile(thtml, Cfg.Settings.PathSrcDone, FileName);
} else {
// Errore nel salvataggio sul DB
cTools.SaveToFile(thtml, Cfg.Settings.PathSrcErrOnSave, FileName);
}
} else {
// Estrazione del racconto fallita
cTools.SaveToFile(thtml, Cfg.Settings.PathSrcErrOnExtract, FileName);
}
} else {
// Link ad un indice
cTools.SaveToFile(thtml, Cfg.Settings.PathIdxDone, FileName);
}
#endregion
Status = eLinkState.End;
App.DB.LinkLog(this);
}
}
#endregion
#region End
if (Status == eLinkState.End) {
}
#endregion
#region Error
if (Status == eLinkState.Error) {
}
#endregion
#region Abort
if (Status == eLinkState.Abort) {
}
#endregion
#region Cancel
if (Status == eLinkState.Cancel) {
}
#endregion
Console.WriteLine("Link.Count=" + newlinks.Count.ToString("00"));
} catch (Exception ex) {
Console.WriteLine($"Errore con {URL}: {ex.Message}");
Status = eLinkState.Error;
App.DB.LinkLog(this);
}
return newlinks;
}
#endregion
}//class cLink_eRac
}//namespace A69WD.Net
+32
View File
@@ -3,6 +3,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using A69WD.Data;
using HtmlAgilityPack;
namespace A69WD.Net {
@@ -42,6 +45,35 @@ namespace A69WD.Net {
eLinkState Status { get; set; }
cRac EstraiRac(HtmlDocument doc);
// #region Gestione Sito
//
// /// <summary>
// /// Booleano per sapere se siamo autenticati al sito
// /// (sempre true per i siti senza autenticazione)
// /// </summary>
// bool Autenticated { get; }
//
// /// <summary>
// /// Esegue l'autenticazione al sito
// /// </summary>
// Task<bool> Authenticate();
//
// /// <summary>
// /// Elaborazione della pagina linkata:
// /// Scarica il link e analizza quanto downloadato
// /// </summary>
// /// <param name="lk"></param>
// /// <returns></returns>
// Task<List<iLink>> CrawlPageAsync(iLink lk);
//
// #endregion
}//interface iLink
}//namespace A69WD.Net
+103 -2
View File
@@ -32,8 +32,109 @@ using System.Runtime.InteropServices;
// È possibile specificare tutti i valori oppure impostare valori predefiniti per i numeri relativi alla revisione e alla build
// usando l'asterisco '*' come illustrato di seguito:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.4.0.0")]
[assembly: AssemblyFileVersion("0.4.0.0")]
[assembly: AssemblyVersion("0.5.3.2")]
[assembly: AssemblyFileVersion("0.5.3.2")]
/*
*
* Vrs 0.5.3.2
*
* Compilata da CSiCoAlm il 2026/01/05
* - Update formattazione testi per eliminare problemi con accenti e caratteri html
* - Update formattazione tags per uniformare quelli di A69 e di Old_Milu
*
*
* ToDo:
* - Aggiungere gestione parallela della FIFO secondo vecchi modello per aumentare il feedback alla UI
* - Inserire anche l'estrazione dei racconti
* - Inserire funzionalità avanzate sul lettore
*
*/
/*
*
* Vrs 0.5.3.1
*
* Compilata da CSiCoAlm il 2026/01/04
* Inserito importazione dei file del vecchio sito Milu che sono completamente non compatibili con i formati attuali.
*
*
* ToDo:
* - Aggiungere gestione parallela della FIFO secondo vecchi modello per aumentare il feedback alla UI
* - Inserire anche l'estrazione dei racconti
* - Inserire funzionalità avanzate sul lettore
*
*/
/*
*
* Vrs 0.5.3.0
*
* Compilata da CSiCoAlm il 2026/01/02
* Inserito nello skraping anche la sorgente eRac
*
*
* ToDo:
* - Aggiungere gestione parallela della FIFO secondo vecchi modello per aumentare il feedback alla UI
* - Inserire anche l'estrazione dei racconti
* - Inserire funzionalità avanzate sul lettore
*
*/
/*
*
* Vrs 0.5.2.0
*
* Compilata da CSiCoAlm il 2026/01/01
* Refactoring del downloader per poterlo utilizzare in ATDB:
* - imglobata la classe iSite nella iLink
* - impostato cartelle di destinazione dei file per distinguere la diagnostica del lavoro
*
*
* ToDo:
* - Aggiungere gestione parallela della FIFO secondo vecchi modello per aumentare il feedback alla UI
* - Inserire anche l'estrazione dei racconti
* - Inserire funzionalità avanzate sul lettore
*
*/
/*
*
* Vrs 0.5.1.0
*
* Compilata da CSiCoAlm il 2026/01/01
* Refactoring del downloader per poterlo utilizzare in ATDB
* Upgradato l'importazione, selezionando casualmente i file da importare,
* gestendo il numero massimo di file e la cartella da importare, sul file di configurazione.
*
*
* ToDo:
* - Aggiungere gestione parallela della FIFO secondo vecchi modello per aumentare il feedback alla UI
* - Inserire anche l'estrazione dei racconti
* - Inserire funzionalità avanzate sul lettore
*
*/
/*
*
* Vrs 0.5.0.0
*
* Compilata da CSiCoAlm il 2025/12/31
* Inserito reader basilare (solo lettura, navigazione random o sequenziale)
* Integrato import e skraping sul reader
*
* ToDo:
* - Aggiungere gestione parallela della FIFO secondo vecchi modello per aumentare il feedback alla UI
* - Inserire anche l'estrazione dei racconti
* - Inserire funzionalità avanzate sul lettore
*
*/
/*
+44 -41
View File
@@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
@@ -8,7 +9,6 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using A69WD.Net;
using A69WD.Site;
@@ -29,37 +29,21 @@ namespace A69WD.Service {
private int _completati = 0;
private int _errori = 0;
// HttpClient autenticato (fornito da AuthService)
private iSiteType _site;
public Service_Crawler(Label lblInCorso, ProgressBar pbStatus) {
_semaphore = new SemaphoreSlim(Cfg.Settings.nThread);
_lblInCorso = lblInCorso;
_progressBar = pbStatus;
}
// Riferimenti ai controlli UI per aggiornare i contatori e la progress bar
private readonly Label _lblInCorso;
//private readonly Label _lblCompletati;
//private readonly Label _lblDaFare;
//private readonly Label _lblErrori;
private readonly ProgressBar _progressBar;
private readonly StatusStrip _sb;
public Service_Crawler(StatusStrip sb) {
_semaphore = new SemaphoreSlim(Cfg.Settings.nThread);
_sb = sb;
}
/// <summary>
/// Avvia il thread worker che consuma la coda di URL.
/// </summary>
public void StartWorker(iSiteType site) {
_site = site;
public void StartWorker() {
_workerThread = new Thread(ProcessQueue) { IsBackground = true };
_workerThread.Start();
}
@@ -82,24 +66,46 @@ namespace A69WD.Service {
/// <summary>
/// Thread worker: consuma la coda ed avvia task di download.
/// </summary>
private void ProcessQueue() {
private async void ProcessQueue() {
// Verifico cartelle di destinazione:
if (!Directory.Exists(Cfg.Settings.PathIdxDone)) Directory.CreateDirectory(Cfg.Settings.PathIdxDone);
if (!Directory.Exists(Cfg.Settings.PathSrcDone)) Directory.CreateDirectory(Cfg.Settings.PathSrcDone);
if (!Directory.Exists(Cfg.Settings.PathSrcErrOnExtract)) Directory.CreateDirectory(Cfg.Settings.PathSrcErrOnExtract);
if (!Directory.Exists(Cfg.Settings.PathSrcErrOnSave)) Directory.CreateDirectory(Cfg.Settings.PathSrcErrOnSave);
// Autenticazione ai siti:
if (!cLink_A69.Autenticated) { await cLink_A69.Authenticate(); }
if (!cLink_eRac.Autenticated) { await cLink_eRac.Authenticate(); }
// Ciclo per ciascun elemento della FIFO:
foreach (iLink link in _urlQueue.GetConsumingEnumerable()) {
Logger.Information("ProcessQueue: Start url " + link.FileName);
Task.Run(async () => {
Interlocked.Increment(ref _inCorso);
UpdateCounters();
await _semaphore.WaitAsync();
try {
List<iLink> urls = await _site.CrawlPageAsync(link);
List<iLink> urls = null;
if (link is cLink_A69) {
cLink_A69 clk = link as cLink_A69;
urls = await clk.CrawlPageAsync();
} else {
if (link is cLink_eRac) {
cLink_eRac clk = link as cLink_eRac;
urls = await clk.CrawlPageAsync();
}
}
Logger.Information("ProcessQueue: end url " + link.FileName + " - URL trovati: " + urls.Count.ToString("000"));
foreach (iLink url in urls) {
foreach (iLink url in urls) {
if (url.Validate) {
// Logger.Warning("URL Validato: " + url.FileName);
// Logger.Warning("URL Validato: " + url.FileName);
App.DB.LinkLog(url);
_urlQueue.Add(url);
//} else {
// Logger.Warning("URL NON Validato: " + url.FileName);
//} else {
// Logger.Warning("URL NON Validato: " + url.FileName);
}
}
} catch (Exception ex) {
@@ -121,30 +127,27 @@ namespace A69WD.Service {
/// Aggiorna i contatori e la progress bar sulla UI.
/// </summary>
private void UpdateCounters() {
if (_lblInCorso.InvokeRequired) {
_lblInCorso.Invoke(new Action(UpdateCounters));
if (_sb.InvokeRequired) {
_sb.Invoke(new Action(UpdateCounters));
return;
}
int daFare = _urlQueue.Count;
_lblInCorso.Text = $"In corso: {_inCorso} - Completati: {_completati} - Errori: {_errori} - Da fare: {daFare}";
int totale = _inCorso + _completati + _errori + daFare;
int fatti = _completati + _errori;
_progressBar.Maximum = totale > 0 ? totale : 1;
_progressBar.Value = fatti;
//_sb.Items["sbStatusLabel"].Text = $"In corso: {_inCorso} - Completati: {_completati} - Errori: {_errori} - Da fare: {daFare}";
_sb.Items["sbStatusLabel"].Text = $"{totale}|{_inCorso}|{fatti}|{_errori}";
_sb.Items["sbStatusLabel"].ToolTipText = "Totali | In Corso | Fatti | Error";
(_sb.Items["sbProgressBar"] as ToolStripProgressBar).Maximum = totale > 0 ? totale : 1;
(_sb.Items["sbProgressBar"] as ToolStripProgressBar).Value = fatti;
}
}
}
+398
View File
@@ -0,0 +1,398 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using A69WD.Data;
using A69WD.Net;
using HtmlAgilityPack;
namespace A69WD.Service {
public class Service_Import_Milu {
// Thread worker che consuma la coda
private Thread _workerThread;
private readonly StatusStrip _sb;
private string _path;
// Contatori
private int _totali = 0;
private int _completati = 0;
private int _duplicati = 0;
private int _errori = 0;
public Service_Import_Milu(StatusStrip sb) {
_sb = sb;
}
public void StartWorker(string path) {
_path = path;
_workerThread = new Thread(ProcessWorking) { IsBackground = true };
_workerThread.Start();
}
public void StopWorker() {
;
}
private async void ProcessWorking() {
// Seleziono 1000 file in modo casuale dalla directory indicata:
// - Ottieni tutti i file nel percorso
string[] files = System.IO.Directory.GetFiles(Cfg.Settings.PathImport, "*chapter=all");
int MaxNFile = files.Length < Cfg.Settings.MaxNumFile ? files.Length : Cfg.Settings.MaxNumFile;
// - Mescola l'elenco dei file in modo casuale e seleziona i primi MaxNFile
// Usiamo LINQ per mescolare (Shuffle) l'array e prendere i primi MaxNFile
// Si noti che mescolare casualmente l'intero array può essere costoso per molte migliaia di file.
// Alternativamente, si può usare un algoritmo di campionamento casuale (come Fisher-Yates) se l'efficienza è cruciale,
// ma per 10 elementi da un elenco ragionevole, questo è semplice ed efficace.
Random rng = new Random();
var fileCasuali = files
.OrderBy(f => rng.Next()) // Ordina in base a un numero casuale
.Take(MaxNFile) // Prendi i primi MaxNFile
.ToList(); // Converti in List<string> (opzionale, ma utile)
// aggiorno contatori
_totali = fileCasuali.Count;
_completati = 0;
_duplicati = 0;
_errori = 0;
UpdateCounters();
// Verifico cartelle di destinazione
string pathExport = System.IO.Path.Combine(Cfg.Settings.PathApplicationRoot, "Export");
if (!Directory.Exists(pathExport)) Directory.CreateDirectory(pathExport);
if (!Directory.Exists(Cfg.Settings.PathSrcDone)) Directory.CreateDirectory(Cfg.Settings.PathSrcDone);
if (!Directory.Exists(Cfg.Settings.PathSrcErrOnExtract)) Directory.CreateDirectory(Cfg.Settings.PathSrcErrOnExtract);
if (!Directory.Exists(Cfg.Settings.PathSrcErrOnSave)) Directory.CreateDirectory(Cfg.Settings.PathSrcErrOnSave);
// Ciclo su ciascun file
foreach (string file in fileCasuali) {
string name = System.IO.Path.GetFileName(file);
Logger.Warning($"Import - Start estrazione racconto: {name}");
List<cRac> racs = await EstraiRacs(file);
if (racs != null) {
bool saveErr = false;
foreach (cRac rac in racs) {
if (rac.Validate()) {
// racconto valido:
// // - lo esporto come xml
// string nam = $"Milu_Rac_{rac.strIDrac}.xml";
// rac.SaveToXmlFile(System.IO.Path.Combine(pathExport, nam));
// - lo salvo sul DB
int res = App.DB.ImportRac(rac); // res=id del racconto nel DB
if (res < 1) {
// errore nel salvataggio
saveErr = true;
break;
} else {
// salvataggio ok
//saveErr = false;
}
} else {
Logger.Warning($"Import - estrazione errata: {name}");
saveErr = true;
break;
}
}
if (saveErr) {
// errore nel salvataggio dei racconti estratti dal file
Logger.Warning($"Import - errore nel salvataggio: {name}");
Interlocked.Increment(ref _errori);
// - Sposto il file
System.IO.File.Move(file, System.IO.Path.Combine(Cfg.Settings.PathSrcErrOnSave, System.IO.Path.GetFileName(file)));
} else {
// salvataggio ok
Logger.Warning($"Import - racconti salvati: {name}");
Interlocked.Increment(ref _completati);
// - Sposto il file
System.IO.File.Move(file, System.IO.Path.Combine(Cfg.Settings.PathSrcDone, System.IO.Path.GetFileName(file)));
}
} else {
Logger.Warning($"Import - estrazione errata: {name}");
Interlocked.Increment(ref _errori);
// - Sposto il file
System.IO.File.Move(file, System.IO.Path.Combine(Cfg.Settings.PathSrcErrOnExtract, System.IO.Path.GetFileName(file)));
}
UpdateCounters();
}
UpdateCounters();
}
private void UpdateCounters() {
if (_sb.InvokeRequired) {
_sb.Invoke(new Action(UpdateCounters));
return;
}
int daFare = _totali - _completati - _errori;
_sb.Items["sbStatusLabel"].Text = $"{_totali}|{_completati}|{_errori}";
_sb.Items["sbStatusLabel"].ToolTipText = "Totali | Completati | Error";
(_sb.Items["sbProgressBar"] as ToolStripProgressBar).Maximum = _totali > 0 ? _totali : 1;
(_sb.Items["sbProgressBar"] as ToolStripProgressBar).Value = _completati + _errori;
}
public async Task<List<cRac>> EstraiRacs(string FileFullPath) {
/*
* Analisi file dei racconti Milu con il vecchio formato:
*
* - I file con un racconto valido hanno la seguente struttura:
*
* {...}
* <div id="pagetitle">
* <a href="viewstory.php?sid={IDrac}">{Titolo}</a>
* by
* <a href="viewuser.php?uid={IDaut}">{Autor}</a>
* </div>
* <div class="infobox">
* <div class="content">
* <span class="label">Summary: </span>{Riassunto}<br />
* <span class="label">Categories:</span>
* <a href='browse.php?type=categories&amp;catid={IDcat}'>{categoria}</a>
* <span class="label">Characters: </span> {Characters}<br />
* <span class='label'>Genres: </span> {Genres}<br />
* <span class='label'>Warnings: </span> {Warnings}<br />
* <span class="label">Challenges:</span> {Challenges}<br />
* <span class="label">Series:</span> {Series}<br />
* <span class="label">Chapters: </span> {Chapters}
* <span class="label">Completed:</span> {Completed}
* <span class="label">Word count:</span> {WordCount}
* <span class="label">Read:</span> {Read}
* <span class="label"> Published: </span>{Published}
* <span class="label">Updated:</span> }Updated}
* </div>
* </div>
* <p><b>1.</b> <a href='#1'>{Tit1}</a> by {Autor}</p>
* <p><b>2.</b> <a href='#2'>{Tit2}</a> by {Autor}</p>
* <p><b>3.</b> <a href='#3'>{Tit3}</a> by {Autor}</p>
* <div class='chaptertitle'><a name='1'></a>{Tit1} by {Autor}</div>
* <div class='chapter'>{Trama1}</div>
* <div class='toplink'><a href='#1'>Torna all'indice</a></div>
* <div class='chaptertitle'><a name='2'></a>{Tit2} by {Autor}</div>
* <div class='chapter'>{Trama2}</div>
* <div class='toplink'><a href='#1'>Torna all'indice</a></div>
* <div class='chaptertitle'><a name='3'></a>{Tit3} by {Autor}</div>
* <div class='chapter'>{Trama3}</div>
* <div class='toplink'><a href='#1'>Torna all'indice</a></div>
* {...}
* <div id="archivedat">Questo racconto è attualmente archiviato su <a href="{URLrac}">{URLrac}</a></div>
*
* Quindi per ricavare i dati del racconto:
* - Ricavo tag pagetitle
* - Se pagetitle vuoto o size<=2kb racconto nullo, inserisco log e proseguo
* - Se pagetitle non nullo allora ho un racconto valido e proseguo l'estrazione.
* - Da pagetitle ricavo il titolo del racconto.
* - dal testo del tag infobox.content ricavo filtrando i vari parametri del racconto
* - Dall'elenco dei tag chaptertitle ricavo i sottotitoli dei singoli racconti ATDB
* - Dall'elenco dei tag chapter ricavo le trame dei singoli racconti.
* - Occorre verificare per bene che l'ordine venga rispettato in modo da ordinarli correttamente.
* - Infine dal tag archivedat.a ricavo l'URL del racconto per verifica che sia come il link.
* - Ricavati tutti i dati inserisco sul DB tutti i singoli capitoli come singoli racconti ATDB assegnando le parti comuni a tutti i racconti.
*
*
*/
cRac rac = new cRac();
List<cRac> racs = new List<cRac>();
HtmlNodeCollection nodes;
try {
Console.WriteLine($"Old Milu - Importo: {FileFullPath}");
string filePathName = System.IO.Path.GetFileName(FileFullPath);
// Ricavo il nodo radice del racconto:
HtmlAgilityPack.HtmlDocument tdoc = new HtmlAgilityPack.HtmlDocument();
tdoc.Load(FileFullPath);
HtmlNode root = tdoc.DocumentNode;
if (root == null) {
// racconto non presente
return null;
}
List<string> origini = null;
List<string> tags = null;
List<cNota> notes = null;
string titolo = string.Empty;
string autore = string.Empty;
DateTime creato = DateTime.Now;
string infoCategories = string.Empty;
string infoPublished = string.Empty;
string infoCharacters = string.Empty;
string infoGenres = string.Empty;
string infoWarnings = string.Empty;
string infoChallenges = string.Empty;
string infoSeries = string.Empty;
string infoChapters = string.Empty;
string infoCompleted = string.Empty;
string infoWordCount = string.Empty;
string infoRead = string.Empty;
#region Info comuni a tutti i capitoli del racconto:
#region Origine
// Origine fissa:
origini = new List<string> { "Old Milu" };
#endregion
#region Titolo & Autore
// Titolo e autore si ricavano assieme: <div id="pagetitle"><a href="viewstory.php?sid={IDrac}">{Titolo}</a> by <a href="viewuser.php?uid={IDaut}">{Autor}</a></div>
try {
nodes = root.SelectNodes("//div[@id='pagetitle']//a[@href]");
foreach (HtmlNode nd in nodes) {
string href = nd.Attributes["href"].Value;
if (href.StartsWith("viewstory.php?sid=")) {
titolo = cTools.FormattaTesto(nd.InnerText.Trim(), false);
} else {
if (href.StartsWith("viewuser.php?uid=")) {
autore = cTools.FormattaTesto(nd.InnerText.Trim(), false);
}
}
}
if (string.IsNullOrEmpty(titolo)) {
Logger.Error($"Old Milu - Import - No Title - {filePathName}");
return null;
} else {
if (string.IsNullOrEmpty(autore)) {
Logger.Error($"Old Milu - Import - No Autor - {filePathName}");
return null;
}
}
} catch (Exception ex) { Logger.Error(string.Format("Fallita estrazione titolo/autore dal file {0}\r\n{1}", filePathName, ex.Message)); return null; }
#endregion
#region Tags: --> Testo dei nodi <div class="infobox">< div class="content"><a href='browse.php?type=categories&amp;catid={IDcat}'>{categoria}</a>
try {
nodes = root.SelectNodes("//div[@class='infobox']//div[@class='content']//a");
List<string> items = new List<string>();
foreach (HtmlNode nd in nodes) {
string href = nd.Attributes["href"].Value;
if (href.StartsWith("browse.php?type=categories")) {
items.Add(cTools.FormattaTesto(nd.InnerText.Replace("genere", "").Trim(), false));
}
}
//items = items
// .OrderBy(s => s) // ordina alfabeticamente
// .Distinct() // elimina duplicati
// .ToList();
cTools.Insert_Format_Tags(ref tags, items);
if (tags.Count<1) {
Logger.Error($"Old Milu - Import - No Tags - {filePathName}");
return null;
}
} catch (Exception ex) { Logger.Warning(string.Format("Fallita estrazione Tags dal file {0}\r\n{1}", filePathName, ex.Message)); return null; }
#endregion
#region Creato & Notes
// Estraggo le informazioni generali
// Data estreatta dal testo del nodo <div class="infobox">< div class="content"> fra "Published{Published}Updated:
try {
HtmlNode nd = root.SelectSingleNode("//div[@class='infobox']//div[@class='content']");
string txt = nd.InnerText.Trim().Replace("&nbsp;", "");
infoPublished = TxtExtract(txt, "Published: ", "Updated: ");
infoCategories = TxtExtract(txt, "Categories:", "Characters:");
infoCharacters = TxtExtract(txt, "Characters:", "Genres:");
infoGenres = TxtExtract(txt, "Genres:", "Warnings:");
infoWarnings = TxtExtract(txt, "Warnings:", "Challenges:");
infoChallenges = TxtExtract(txt, "Challenges:", "Series:");
infoSeries = TxtExtract(txt, "Series:", "Chapters");
infoChapters = TxtExtract(txt, "Chapters:", "Completed");
infoCompleted = TxtExtract(txt, "Completed:", "Word count:");
infoWordCount = TxtExtract(txt, "Word count:", "Read:");
infoRead = TxtExtract(txt, "Read:", "Published:");
} catch (Exception ex) { Logger.Warning(string.Format("Informazioni generali non riconosciute nel file {0}\r\n{1}", filePathName, ex.Message)); return null; }
// Data del racconto
try {
CultureInfo culture = CultureInfo.CreateSpecificCulture("it-IT");
DateTimeStyles styles = DateTimeStyles.None;
if (!DateTime.TryParse(infoPublished.Trim(), culture, styles, out creato)) {
Logger.Error($"Old Milu - Import - No data - {filePathName}");
}
} catch (Exception ex) { Logger.Warning(string.Format("Data non riconosciuta nel file {0}\r\n{1}", filePathName, ex.Message)); return null; }
// Notes (comuni fra tutti i racconti):
try {
notes = new List<Data.cNota>();
notes.Add(new Data.cNota("SrcFile", System.IO.Path.GetFileName(filePathName)));
notes.Add(new Data.cNota("infoCategories", infoCategories));
notes.Add(new Data.cNota("infoCharacters", infoCharacters));
notes.Add(new Data.cNota("infoGenres", infoGenres));
notes.Add(new Data.cNota("infoWarnings", infoWarnings));
notes.Add(new Data.cNota("infoChallenges", infoChallenges));
notes.Add(new Data.cNota("infoSeries", infoSeries));
notes.Add(new Data.cNota("infoChapters", infoChapters));
notes.Add(new Data.cNota("infoCompleted", infoCompleted));
notes.Add(new Data.cNota("infoWordCount", infoWordCount));
notes.Add(new Data.cNota("infoRead", infoRead));
notes.Add(new Data.cNota("infoPublished", infoPublished));
} catch (Exception ex) { Logger.Warning(string.Format("Notes non riconosciute nel file {0}\r\n{1}", filePathName, ex.Message)); return null; }
#endregion
#endregion
// I vari capitoli dei racconti sono descritti dai nodi <div class='chaptertitle'> e <div class='chapter'> in successione.
try {
HtmlNodeCollection capTitles = root.SelectNodes("//div[@class='chaptertitle']");
HtmlNodeCollection capText = root.SelectNodes("//div[@class='chapter']");
if ((capTitles.Count > 0) && (capTitles.Count == capText.Count)) {
// Trovato capitoli congruenti
for (int i = 0; i < capTitles.Count; i++) {
// Creo i racconti:
rac = new cRac();
rac.Title = titolo;
rac.SubTitle = cTools.FormattaTesto(capTitles[i].InnerText.Trim().Replace($"by {autore}","").Trim(),true);
rac.Creato = creato;
rac.Updated = DateTime.Now;
//rac.Trama = cTools.FormattaTesto(capText[i].InnerHtml.Trim(), false);
rac.Trama = cTools.FormattaTesto(capText[i].InnerHtml.Trim(), true);
rac.Voto = "Non Letto";
rac.Autori = new List<string>();
rac.Autori.Add(autore);
rac.Tags = tags;
rac.Origini = origini;
rac.Notes = notes;
// Inserisco il racconto nella collezione
if (rac.Validate()) { racs.Add(rac); }
}
} else {
// Capitoli non trovati o non congruenti
Logger.Error($"Old Milu - Import - No Capitoli - {filePathName}");
return null;
}
} catch (Exception ex) { Logger.Warning(string.Format("Fallita estrazione capitolo {0}\r\n{1}", filePathName, ex.Message)); return null; }
Logger.Information($"Terminato estrazione racconti dal file: {filePathName}");
} catch (Exception ex) {
Logger.Error($"Old Milu - Import Error - {FileFullPath}: {ex.Message}");
Console.WriteLine($"Old Milu - Import Error - {FileFullPath}: {ex.Message}");
return null;
}
return racs;
}
protected string TxtExtract(string src, string start, string end) {
int nstart = src.LastIndexOf(start);
int nstop = src.LastIndexOf(end);
int offset = start.Length;
return src.Substring(nstart + offset, nstop - nstart - offset).Trim();
}
}//class Service_Import_Milu
}//namespace A69WD.Service
+54 -23
View File
@@ -8,7 +8,6 @@ using System.Threading.Tasks;
using System.Windows.Forms;
using A69WD.Data;
using A69WD.Net;
using A69WD.Site;
@@ -19,17 +18,16 @@ namespace A69WD.Service {
// Thread worker che consuma la coda
private Thread _workerThread;
private readonly Label _lblInCorso;
private readonly ProgressBar _progressBar;
private readonly StatusStrip _sb;
private string _path;
// Contatori
private int _totali = 0;
private int _completati = 0;
private int _duplicati = 0;
private int _errori = 0;
public Service_Importer(Label lblInCorso, ProgressBar pbStatus) {
_lblInCorso = lblInCorso;
_progressBar = pbStatus;
public Service_Importer(StatusStrip sb) {
_sb = sb;
}
public void StartWorker(string path) {
@@ -45,12 +43,11 @@ namespace A69WD.Service {
private async void ProcessWorking() {
string[] files = System.IO.Directory.GetFiles(_path, "A69_*.html");
_totali = files.Length;
_completati = 0;
_errori = 0;
UpdateCounters();
//string[] files = System.IO.Directory.GetFiles(_path, "A69_*.html");
//_totali = files.Length;
//_completati = 0;
//_errori = 0;
//UpdateCounters();
// foreach (string file in files) {
// Task.Run(async () => {
// Logger.Warning($"Start estrazione racconto: {file}");
@@ -74,7 +71,35 @@ namespace A69WD.Service {
// });
// }
foreach (string file in files) {
// Seleziono 1000 file in modo casuale dalla directory indicata:
// - Ottieni tutti i file nel percorso
string[] files = System.IO.Directory.GetFiles(Cfg.Settings.PathImport, "A69_*.html");
int MaxNFile = files.Length < Cfg.Settings.MaxNumFile ? files.Length : Cfg.Settings.MaxNumFile;
// - Mescola l'elenco dei file in modo casuale e seleziona i primi MaxNFile
// Usiamo LINQ per mescolare (Shuffle) l'array e prendere i primi MaxNFile
// Si noti che mescolare casualmente l'intero array può essere costoso per molte migliaia di file.
// Alternativamente, si può usare un algoritmo di campionamento casuale (come Fisher-Yates) se l'efficienza è cruciale,
// ma per 10 elementi da un elenco ragionevole, questo è semplice ed efficace.
Random rng = new Random();
var fileCasuali = files
.OrderBy(f => rng.Next()) // Ordina in base a un numero casuale
.Take(MaxNFile) // Prendi i primi MaxNFile
.ToList(); // Converti in List<string> (opzionale, ma utile)
// aggiorno contatori
_totali = fileCasuali.Count;
_completati = 0;
_duplicati = 0;
_errori = 0;
UpdateCounters();
// Verifico cartelle di destinazione
if (!Directory.Exists(Cfg.Settings.PathSrcDone)) Directory.CreateDirectory(Cfg.Settings.PathSrcDone);
if (!Directory.Exists(Cfg.Settings.PathSrcErrOnExtract)) Directory.CreateDirectory(Cfg.Settings.PathSrcErrOnExtract);
if (!Directory.Exists(Cfg.Settings.PathSrcErrOnSave)) Directory.CreateDirectory(Cfg.Settings.PathSrcErrOnSave);
// Ciclo su ciascun file
foreach (string file in fileCasuali) {
string name = System.IO.Path.GetFileName(file);
Logger.Warning($"Import - Start estrazione racconto: {name}");
@@ -82,39 +107,45 @@ namespace A69WD.Service {
cRac rac = await lk.EstraiRac(file);
if (rac != null) {
// Racconto estratto correttamente, lo salvo sul DB
Logger.Warning($"Import - estrazione corretta: {name}");
int res = App.DB.ImportRac(rac);
if (res != 1) {
Logger.Warning($"Import - estrazione corretta: {name}");
int res = App.DB.ImportRac(rac); // res=id del racconto nel DB
if (res < 1) {
// errore nel salvataggio
Logger.Warning($"Import - errore nel salvataggio: {name}");
Interlocked.Increment(ref _errori);
// - Sposto il file
System.IO.File.Move(file, System.IO.Path.Combine(Cfg.Settings.PathSrcErrOnSave, System.IO.Path.GetFileName(file)));
} else {
// salvataggio ok
Logger.Warning($"Import - racconto salvato: {name}");
Interlocked.Increment(ref _completati);
// - Sposto il file
System.IO.File.Move(file, System.IO.Path.Combine(Cfg.Settings.PathSrcDone, System.IO.Path.GetFileName(file)));
}
} else {
Logger.Warning($"Import - estrazione errata: {name}");
Logger.Warning($"Import - estrazione errata: {name}");
Interlocked.Increment(ref _errori);
// - Sposto il file
System.IO.File.Move(file, System.IO.Path.Combine(Cfg.Settings.PathSrcErrOnExtract, System.IO.Path.GetFileName(file)));
}
UpdateCounters();
}
UpdateCounters();
}
private void UpdateCounters() {
if (_lblInCorso.InvokeRequired) {
_lblInCorso.Invoke(new Action(UpdateCounters));
if (_sb.InvokeRequired) {
_sb.Invoke(new Action(UpdateCounters));
return;
}
int daFare = _totali - _completati - _errori;
_lblInCorso.Text = $"Totali: {_totali} - Completati: {_completati} - Errori: {_errori} - Da fare: {daFare}";
_sb.Items["sbStatusLabel"].Text = $"{_totali}|{_completati}|{_errori}";
_sb.Items["sbStatusLabel"].ToolTipText = "Totali | Completati | Error";
_progressBar.Maximum = _totali > 0 ? _totali : 1;
_progressBar.Value = _completati + _errori;
(_sb.Items["sbProgressBar"] as ToolStripProgressBar).Maximum = _totali > 0 ? _totali : 1;
(_sb.Items["sbProgressBar"] as ToolStripProgressBar).Value = _completati + _errori;
}
+7 -1
View File
@@ -6,6 +6,12 @@
<!-- <PathApplicationRoot>C:\Temporary\A69WD</PathApplicationRoot> -->
<PathApplicationRoot>C:\Commesse\_zPrivate\A69WD\Test</PathApplicationRoot>
<!-- Path della cartella con i file da importare -->
<PathImport>C:\Commesse\_zPrivate\A69WD\Test\Import</PathImport>
<!-- Numero di file massimo da gestire. -->
<MaxNumFile>5</MaxNumFile>
<!-- Nome del database usato dall'applicazione. -->
<DBName>A69DB.db</DBName>
@@ -20,7 +26,7 @@
<TextEditor>C:\Program Files\Notepad++\notepad++.exe</TextEditor>
<!-- Numero di thread secondari. -->
<nThread>9</nThread>
<nThread>3</nThread>
</Cfg>
-299
View File
@@ -1,299 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using A69WD.Net;
using HtmlAgilityPack;
namespace A69WD.Site {
public class cSite_A69 : iSiteType {
private readonly HttpClient client;
private readonly string nickname = @"Toky78";
private readonly string password = @"p!fw05Q9Hc6j5";
private bool logged = false;
public bool Logged => logged;
/// <summary>
/// Esegue l'autenticazione al sito:
/// - Scarica la pagina di autenticazione
/// - analizza quanto scaricato per determinare i campi dinamici 'ts' e 'token'
/// - prepara la risposta al form di identificazione
/// - invia il POST di autenticazione
/// - analizza la risposta per virificare il corretto login
/// </summary>
public async Task<bool> Authenticate() {
try {
// 1) GET pagina di login
Console.WriteLine("A69 - Richiedo la pagina di login...");
string loginPageHtml = await client.GetStringAsync(cLink_A69.LoginPageUrl);
// 2) Parsing HTML per estrarre ts e token
var doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(loginPageHtml);
string ts = doc.DocumentNode
.SelectSingleNode("//input[@name='ts']")
?.GetAttributeValue("value", "");
string token = doc.DocumentNode
.SelectSingleNode("//input[@name='token']")
?.GetAttributeValue("value", "");
if (string.IsNullOrWhiteSpace(ts) || string.IsNullOrWhiteSpace(token)) {
Console.WriteLine("A69 - Impossibile estrarre 'ts' o 'token' dalla pagina di login.");
return logged;
}
Console.WriteLine($"A69 - Parametri dinamici trovati: ts={ts}, token={token}");
// 3) POST login con valori dinamici
var loginData = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string,string>("nick", nickname),
new KeyValuePair<string,string>("pwd", password),
new KeyValuePair<string,string>("ricordami","1"),
new KeyValuePair<string,string>("login", "Entra"),
new KeyValuePair<string,string>("ts", ts),
new KeyValuePair<string,string>("token", token)
});
Console.WriteLine("Invio credenziali di login...");
var loginResponse = await client.PostAsync(cLink_A69.LoginPostUrl, loginData);
if (!loginResponse.IsSuccessStatusCode) {
Console.WriteLine($"A69 - Login fallito. HTTP {(int)loginResponse.StatusCode} {loginResponse.ReasonPhrase}");
return logged;
}
// Verifica semplice: sulla pagina di login effettuato, ricerco il nome dell'utente:
string loginResult = await loginResponse.Content.ReadAsStringAsync();
if (loginResult.Contains(nickname)) {
Console.WriteLine("A69 - Login effettuato con successo! Sessione autenticata attiva.");
logged = true;
} else {
Console.WriteLine("A69 - Login non riuscito: il server ha restituito un messaggio di errore.");
logged = false;
}
} catch (Exception ex) {
Console.WriteLine($"Errore generale: {ex.Message}");
}
return logged;
}
/// <summary>
/// Codice da eseguire per ciascun A69 link:
/// - scarico l'url indicato
/// - se richiesto lo salvo come file di testo
/// - analizzo il documento html ed estraggo tutti gli url ad altre pagine di link
/// - analizzo il documento html ed estraggo tutti gli url a racconti
/// - analizzo il documento html ed estraggo il testo del racconto
/// </summary>
/// <param name="lk"></param>
/// <returns></returns>
public async Task<List<iLink>> CrawlPageAsync(iLink lk) {
var newlinks = new List<iLink>();
try {
Console.WriteLine($"A69 - Scarico: {lk.URL}");
HtmlDocument tdoc = null;
#region ToConfirm
//if (lk.Status == eLinkState.ToConfirm) {
// // Eseguo la verifica se link da downloadare
// eLinkState tstate = App.DB.GetStateByURL(lk.URL);
// if (
// (tstate == eLinkState.ToConfirm) ||
// (tstate == eLinkState.ToDownload) ||
// (tstate == eLinkState.Error)
// ) {
// lk.Status = eLinkState.ToDownload;
// App.DB.LinkLog(lk);
// } else {
// lk.Status = eLinkState.End;
// App.DB.LinkLog(lk);
// }
//}
if (lk.Status == eLinkState.ToConfirm) {
// Eseguo la verifica se link da downloadare
lk.Status = eLinkState.ToDownload;
}
#endregion
#region ToDownload
if (lk.Status == eLinkState.ToDownload) {
lk.Status = eLinkState.OnDownload;
}
#endregion
#region OnDownload
if (lk.Status == eLinkState.OnDownload) {
// Scarico il file
var resp = await client.GetAsync(lk.URL);
if (!resp.IsSuccessStatusCode) {
Console.WriteLine($"A69 - Errore HTTP {(int)resp.StatusCode} {resp.ReasonPhrase} per {lk.URL}");
lk.Status = eLinkState.Error;
//App.DB.LinkLog(lk);
return newlinks;
}
// Scarico la pagina
var thtml = await client.GetStringAsync(lk.URL);
tdoc = new HtmlDocument();
tdoc.LoadHtml(thtml);
// Salvo il file downloadato
//System.IO.StreamWriter tsw = new System.IO.StreamWriter(lk.DstFilePath, false, Encoding.UTF8);
System.IO.StreamWriter tsw = new System.IO.StreamWriter(System.IO.Path.Combine(lk.FilePath, lk.FileName), false, Encoding.UTF8);
tsw.Write(thtml);
lk.Status = eLinkState.ToAnalize;
}
#endregion
#region ToAnalize
{
if (lk.Status == eLinkState.ToAnalize) {
List<string> tmpLk = new List<string>();
#region Estraggo gli url ad indici:
{
/*
* Gli url delle pagine di indice sono del tipo:
*
* <div id="pagination">
* <ul>
* <li class="prev"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=2">&lsaquo;</a></li>
* <li class="page"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=1">1</a></li>
* <li class="page"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=2">2</a></li>
* <li class="currentpage">3</li>
* <li class="page"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=4">4</a></li>
* <li class="page"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=5">5</a></li>
* <li class="next"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=4">&rsaquo;</a></li>
* </ul>
* </div>
*
*/
// Estraggo i link alle altre pagine:
var tnodes = tdoc.DocumentNode.SelectNodes("//div[@id='pagination']//ul//li//a[@href]");
if (tnodes != null) {
foreach (var link in tnodes) {
var href = link.GetAttributeValue("href", "");
if (!string.IsNullOrEmpty(href) && href.StartsWith("/racconti-erotici/racconti_lista.php?genere="))
tmpLk.Add(href); //tmpLk.Add(cLink_A69.PageBase + href);
}
}
// Rimuove duplicati e ordina alfabeticamente
tmpLk = tmpLk
.OrderBy(s => s) // ordina alfabeticamente
.Distinct() // elimina duplicati
.ToList();
// Inserisco i link trovati
try {
foreach (string ilk in tmpLk) {
string[] separator = new string[] { "&pagina=" };
string[] txt = ilk.Split(separator, StringSplitOptions.RemoveEmptyEntries);
string url = cLink_A69.PageBase + ilk;
string name = txt[0].Replace("/racconti-erotici/racconti_lista.php?genere=", "_A69_") + "_" + int.Parse(txt[1]).ToString("0000") + ".html";
newlinks.Add(new cLink_A69(url, name));
}
} catch (Exception ex) { Logger.Error("Errore analisi Idx link dal " + lk.FileName + " - " + ex.Message); }
}
#endregion
#region Estraggo gli url a racconti:
{
/*
*
* I link ai racconti sono dei pulsanti così formati:
* <div class="continua">
* <a href="/racconti-erotici/altro/Tutto-e-iniziato-per-gioco-pazza-famiglia_166770.html">Continua a leggere » </a>
* </div>
*/
// Estraggo i link ai racconti:
tmpLk = new List<string>();
// Estraggo i link alle altre pagine:
var tnodes = tdoc.DocumentNode.SelectNodes("//div[@class='continua']//a[@href]");
if (tnodes != null) {
foreach (var link in tnodes) {
var href = link.GetAttributeValue("href", "");
if (!string.IsNullOrEmpty(href) && href.StartsWith("/racconti-erotici/") && (href.EndsWith(".html") || href.EndsWith(".htm")))
tmpLk.Add(href); //tmpLk.Add(cLink_A69.PageBase + href);
}
}
// Rimuove duplicati e ordina alfabeticamente
tmpLk = tmpLk
.OrderBy(s => s) // ordina alfabeticamente
.Distinct() // elimina duplicati
.ToList();
// Inserisco i link trovati
try {
foreach (string ilk in tmpLk) {
string url = cLink_A69.PageBase + ilk;
string name = ilk.Replace("/racconti-erotici/", "A69_").Replace(@"/","_");
newlinks.Add(new cLink_A69(url, name));
}
} catch (Exception ex) { Logger.Error("Errore analisi Idx link dal " + lk.FileName + " - " + ex.Message); }
}
#endregion
lk.Status = eLinkState.End;
App.DB.LinkLog(lk);
}
}
#endregion
#region End
if (lk.Status == eLinkState.End) {
}
#endregion
#region Error
if (lk.Status == eLinkState.Error) {
}
#endregion
#region Abort
if (lk.Status == eLinkState.Abort) {
}
#endregion
#region Cancel
if (lk.Status == eLinkState.Cancel) {
}
#endregion
Console.WriteLine("Link.Count=" + newlinks.Count.ToString("00"));
} catch (Exception ex) {
Console.WriteLine($"Errore con {lk.URL}: {ex.Message}");
lk.Status = eLinkState.Error;
//App.DB.LinkLog(lk);
}
return newlinks;
}
public cSite_A69() {
var handler = new HttpClientHandler {
CookieContainer = new CookieContainer(),
UseCookies = true,
AllowAutoRedirect = true,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
client = new HttpClient(handler);
client.Timeout = TimeSpan.FromSeconds(60);
client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
client.DefaultRequestHeaders.Referrer = new Uri(cLink_A69.LoginPageUrl);
}
}//class cSite_A69
}//namespace A69WD.Site
-54
View File
@@ -1,54 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using A69WD.Net;
/*
*
* Interfaccia iSiteType
* ---------------------
* Questa interfaccia definisce il contratto per le diverse tipologie di siti gestiti dal crawler.
* Ogni sito deve implementare:
* - Authenticate(): eventuale autenticazione al sito (PUT, POST, ecc.)
* - ValidateLink(string href): regole di validazione dei link estratti
* - CrawlPageAsync(string url): scarica una pagina e la analizza
*
* In questo modo il programma principale può distinguere facilmente tra siti autenticati e pubblici,
* mantenendo il codice modulare ed estensibile.
*
*/
namespace A69WD.Site {
/// <summary>
/// Interfaccia per gestire le operazioni da fare su un sito
/// </summary>
public interface iSiteType {
/// <summary>
/// Booleano per sapere se siamo autenticati al sito
/// (sempre true per i siti senza autenticazione)
/// </summary>
bool Logged { get; }
/// <summary>
/// Esegue l'autenticazione al sito
/// </summary>
Task<bool> Authenticate();
/// <summary>
/// Elaborazione della pagina linkata:
/// Scarica il link e analizza quanto downloadato
/// </summary>
/// <param name="lk"></param>
/// <returns></returns>
Task<List<iLink>> CrawlPageAsync(iLink lk);
}//interface iSiteType
}//namespace A69WD.Site
-140
View File
@@ -1,140 +0,0 @@
namespace A69WD {
partial class frmDownloader {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.btnClose = new System.Windows.Forms.Button();
this.btnDownloadAll = new System.Windows.Forms.Button();
this.lblST = new System.Windows.Forms.Label();
this.btnImportLinks = new System.Windows.Forms.Button();
this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
this.btnShowLog = new System.Windows.Forms.Button();
this.pbST = new System.Windows.Forms.ProgressBar();
this.btnImport = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// btnClose
//
this.btnClose.Location = new System.Drawing.Point(348, 12);
this.btnClose.Name = "btnClose";
this.btnClose.Size = new System.Drawing.Size(79, 33);
this.btnClose.TabIndex = 14;
this.btnClose.Text = "Close";
this.btnClose.UseVisualStyleBackColor = true;
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
//
// btnDownloadAll
//
this.btnDownloadAll.Location = new System.Drawing.Point(12, 12);
this.btnDownloadAll.Name = "btnDownloadAll";
this.btnDownloadAll.Size = new System.Drawing.Size(79, 33);
this.btnDownloadAll.TabIndex = 11;
this.btnDownloadAll.Text = "Start A69";
this.btnDownloadAll.UseVisualStyleBackColor = true;
this.btnDownloadAll.Click += new System.EventHandler(this.btnDownloadAll_Click);
//
// lblST
//
this.lblST.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.lblST.Dock = System.Windows.Forms.DockStyle.Bottom;
this.lblST.Location = new System.Drawing.Point(0, 73);
this.lblST.Name = "lblST";
this.lblST.Size = new System.Drawing.Size(433, 22);
this.lblST.TabIndex = 9;
this.lblST.Text = "Status";
this.lblST.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// btnImportLinks
//
this.btnImportLinks.Enabled = false;
this.btnImportLinks.Location = new System.Drawing.Point(97, 12);
this.btnImportLinks.Name = "btnImportLinks";
this.btnImportLinks.Size = new System.Drawing.Size(79, 33);
this.btnImportLinks.TabIndex = 15;
this.btnImportLinks.Text = "From DB";
this.btnImportLinks.UseVisualStyleBackColor = true;
this.btnImportLinks.Click += new System.EventHandler(this.btnImportLinks_Click);
//
// openFileDialog1
//
this.openFileDialog1.FileName = "openFileDialog1";
this.openFileDialog1.Filter = "File Testo|*.tes|Tutti i file|*.*";
//
// btnShowLog
//
this.btnShowLog.Location = new System.Drawing.Point(263, 12);
this.btnShowLog.Name = "btnShowLog";
this.btnShowLog.Size = new System.Drawing.Size(79, 33);
this.btnShowLog.TabIndex = 17;
this.btnShowLog.Text = "Show Log";
this.btnShowLog.UseVisualStyleBackColor = true;
this.btnShowLog.Click += new System.EventHandler(this.btnShowLog_Click);
//
// pbST
//
this.pbST.Dock = System.Windows.Forms.DockStyle.Bottom;
this.pbST.Location = new System.Drawing.Point(0, 55);
this.pbST.Name = "pbST";
this.pbST.Size = new System.Drawing.Size(433, 18);
this.pbST.TabIndex = 18;
//
// btnImport
//
this.btnImport.Location = new System.Drawing.Point(182, 12);
this.btnImport.Name = "btnImport";
this.btnImport.Size = new System.Drawing.Size(79, 33);
this.btnImport.TabIndex = 19;
this.btnImport.Text = "Import Rac";
this.btnImport.UseVisualStyleBackColor = true;
this.btnImport.Click += new System.EventHandler(this.btnImport_Click);
//
// frmDownloader
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(433, 95);
this.Controls.Add(this.btnImport);
this.Controls.Add(this.pbST);
this.Controls.Add(this.btnShowLog);
this.Controls.Add(this.btnImportLinks);
this.Controls.Add(this.btnClose);
this.Controls.Add(this.btnDownloadAll);
this.Controls.Add(this.lblST);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Name = "frmDownloader";
this.Text = "frmDownloader";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button btnClose;
private System.Windows.Forms.Button btnDownloadAll;
private System.Windows.Forms.Label lblST;
private System.Windows.Forms.Button btnImportLinks;
private System.Windows.Forms.OpenFileDialog openFileDialog1;
private System.Windows.Forms.Button btnShowLog;
private System.Windows.Forms.ProgressBar pbST;
private System.Windows.Forms.Button btnImport;
}
}
-189
View File
@@ -1,189 +0,0 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
using System.Net;
using static System.Windows.Forms.LinkLabel;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using A69WD.Site;
using A69WD.Net;
using System.Collections.Concurrent;
using System.Xml.Linq;
using A69WD.Service;
using A69WD.Data;
namespace A69WD {
public partial class frmDownloader : Form {
// Coda thread-safe di URL da scaricare
private readonly BlockingCollection<iLink> _urlQueue = new BlockingCollection<iLink>(new ConcurrentQueue<iLink>());
// Semaforo per limitare il numero massimo di download simultanei
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);
public frmDownloader() {
InitializeComponent();
this.Text = String.Format("A69WD - Dowloader Testi Vrs {0}", cTools.AssemblyVersion);
//engine = new cA69Engine();
//engine.Updating += Engine_Updating;
}
//cA69Engine engine = null;
/// <summary>
/// Metodo per scansionare i link da un file di testo ed inserirli come da downloadare se non ci sono già
/// </summary>
private void btnImportLinks_Click(object sender, EventArgs e) {
//// Chiedo il file di testo da caricare
//lblST.Text = " ";
//this.openFileDialog1.InitialDirectory = Cfg.Settings.PathApplicationRoot;
//if (this.openFileDialog1.ShowDialog() == DialogResult.OK) {
// lblST.Text = string.Format("Importati {0} link", engine.ImportLinkFromFile(openFileDialog1.FileNames[0]));
//}
}
private async void btnDownloadAll_Click(object sender, EventArgs e) {
btnDownloadAll.Enabled = false;
// - Creo istanza del client per il sito con autenticazione:
lblST.Text = DateTime.Now.ToString() + " - Login";
A69WD.Site.iSiteType siteA69 = new cSite_A69();
// - Eseguo l'autenticazione
bool res = await siteA69.Authenticate();
if (res) {
lblST.Text = DateTime.Now.ToString() + " - sito loggato: " + res.ToString();
Logger.Information("Sito correttamente loggato.");
} else {
Logger.Information("Errore di autenticazione al sito.");
return;
}
// - Verifico path per il download
if (!System.IO.Directory.Exists(Cfg.Settings.PathDownloadWorking)) System.IO.Directory.CreateDirectory(Cfg.Settings.PathDownloadWorking);
// // Test downloadando tutti i 10 primi link per categoria
// List<iLink> links = cLink_A69.GetStartLink();
// int i = 1;
// foreach (iLink link in links) {
// lblST.Text = "Start url " + link.FileName;
// Logger.Information("Start url " + link.FileName);
// await siteA69.CrawlPageAsync(link);
// Logger.Information(" end url " + link.FileName);
// i++;
// }
// // Inserisco i link iniziali sulla coda dei download:
// if (!cLink_A69.InsertStartLink(_urlQueue)) {
// lblST.Text = "Inserimento A69 start link fault.";
// return;
// }
// lblST.Text = "In coda vi sono: " + _urlQueue.Count.ToString() + " url.";
//
// // Test estrazione link dal primo indice:
// iLink link = new cLink_A69("https://www.annunci69.it/racconti-erotici/racconti_lista.php?genere=altro&pagina=1", "Idx_altro_1.html");
// Logger.Information("Start url " + link.FileName);
// List<iLink> urls = await siteA69.CrawlPageAsync(link);
// Logger.Information(" end url " + link.FileName + " - URL trovati: " + urls.Count.ToString("000"));
// foreach (iLink url in urls) { _urlQueue.Add(url); }
// lblST.Text = "In coda vi sono: " + _urlQueue.Count.ToString() + " url.";
// Preparo il servizio
serviceCrawler = new Service_Crawler(lblST, pbST);
// Inserisco i link iniziali nella coda
if (!cLink_A69.InsertStartLink(serviceCrawler)) {
lblST.Text = "Inserimento A69 start link fault.";
return;
}
// avvio il servizio
serviceCrawler.StartWorker(siteA69);
}
private Service_Crawler serviceCrawler = null;
private void btnShowLog_Click(object sender, EventArgs e) {
cTools.ShowOnNotepad(Cfg.Settings.PathFileLogs);
}
private void btnHide_Click(object sender, EventArgs e) {
if (MessageBox.Show("Metodo ancora da terminare, la finestra non sarà più visibile, killare il processo", "ATTENZIONE", MessageBoxButtons.OKCancel) == DialogResult.OK){
this.Hide();
}
}
private void btnClose_Click(object sender, EventArgs e) {
//if (engine.Stop()) this.Close();
// fermo i servizi
if (serviceCrawler != null) serviceCrawler.StopWorker();
if (serviceImporter != null) serviceImporter.StopWorker();
this.Close();
}
#region Gestione eventi dal engine
//
// private void Engine_Updating(object sender, EngineEventArgs e) { EngineUpdatingStatus(e.Message, e.NTot, e.NtoDown, e.NDown); }
//
// delegate void EngineUpdatingStatusCallback(string Message, int NTot, int NtoDown, int NDown);
// private void EngineUpdatingStatus(string Message, int NTot, int NtoDown, int NDown) {
// try {
// // InvokeRequired required compares the thread ID of the
// // calling thread to the thread ID of the creating thread.
// // If these threads are different, it returns true.
// if (lblST.InvokeRequired) {
// EngineUpdatingStatusCallback callBack = new EngineUpdatingStatusCallback(EngineUpdatingStatus);
// this.Invoke(callBack, new object[] { Message, NTot, NtoDown, NDown });
// } else {
// lblST.Text = string.Format("{4} - {3} | {2} | {1} - {0}", Message, NTot, NtoDown, NDown, DateTime.Now.ToString("HH:mm:ss"));
// Logger.Information(lblST.Text);
// }
// } catch (Exception ex) {
// Logger.Log(new object[] { "Error Code: ER05553", "Errore in EngineUpdatingStatus():", ex });
// }
// }
//
#endregion
private void btnImport_Click(object sender, EventArgs e) {
// string fp = "C:\\Commesse\\_zPrivate\\A69WD\\Test\\Down\\__A69_altro_Collarini-e-fantasie_166756.html";
// cLink_A69 lk = new cLink_A69("Tmp", "tmp");
// cRac rac = await lk.EstraiRac(fp);
// if (rac != null) {
// //foreach (string tag in rac.Tags) {
// for (int i = 0; i<rac.Tags.Count; i++) {
// Console.WriteLine(rac.Tags[i]);
// }
// // Racconto estratto correttamente, lo salvo sul DB
// App.DB.ImportRac(rac);
// }
// Preparo il servizio
serviceImporter = new Service_Importer(lblST, pbST);
// Per ora uso la directory di default, poi metteremo selezione
string path = Cfg.Settings.PathDownloadWorking;
// avvio il servizio
serviceImporter.StartWorker(path);
}
private Service_Importer serviceImporter = null;
}//class frmDownloader
}//namespace A69WD
+197
View File
@@ -0,0 +1,197 @@
namespace A69WD.UI {
partial class frmReader {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.rtb = new System.Windows.Forms.RichTextBox();
this.sb = new System.Windows.Forms.StatusStrip();
this.sbMessage = new System.Windows.Forms.ToolStripStatusLabel();
this.sbProgressBar = new System.Windows.Forms.ToolStripProgressBar();
this.sbStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.btnTest1 = new System.Windows.Forms.Button();
this.pnlTop = new System.Windows.Forms.Panel();
this.btnTest2 = new System.Windows.Forms.Button();
this.btnTest4 = new System.Windows.Forms.Button();
this.btnTest3 = new System.Windows.Forms.Button();
this.btnTest5 = new System.Windows.Forms.Button();
this.sb.SuspendLayout();
this.pnlTop.SuspendLayout();
this.SuspendLayout();
//
// rtb
//
this.rtb.BackColor = System.Drawing.Color.White;
this.rtb.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.rtb.Dock = System.Windows.Forms.DockStyle.Fill;
this.rtb.Font = new System.Drawing.Font("Verdana", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.rtb.Location = new System.Drawing.Point(0, 42);
this.rtb.Name = "rtb";
this.rtb.Size = new System.Drawing.Size(700, 378);
this.rtb.TabIndex = 2;
this.rtb.Text = "";
this.rtb.KeyDown += new System.Windows.Forms.KeyEventHandler(this.KeyDownAnalisi);
//
// sb
//
this.sb.AutoSize = false;
this.sb.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.sbMessage,
this.sbProgressBar,
this.sbStatusLabel});
this.sb.Location = new System.Drawing.Point(0, 420);
this.sb.Name = "sb";
this.sb.Size = new System.Drawing.Size(700, 24);
this.sb.TabIndex = 1;
this.sb.Text = "statusStrip1";
//
// sbMessage
//
this.sbMessage.BorderSides = ((System.Windows.Forms.ToolStripStatusLabelBorderSides)((((System.Windows.Forms.ToolStripStatusLabelBorderSides.Left | System.Windows.Forms.ToolStripStatusLabelBorderSides.Top)
| System.Windows.Forms.ToolStripStatusLabelBorderSides.Right)
| System.Windows.Forms.ToolStripStatusLabelBorderSides.Bottom)));
this.sbMessage.BorderStyle = System.Windows.Forms.Border3DStyle.SunkenInner;
this.sbMessage.Name = "sbMessage";
this.sbMessage.Size = new System.Drawing.Size(483, 19);
this.sbMessage.Spring = true;
this.sbMessage.Tag = "Messaggi";
this.sbMessage.Text = "Messaggi";
this.sbMessage.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.sbMessage.ToolTipText = "Messaggio";
//
// sbProgressBar
//
this.sbProgressBar.Name = "sbProgressBar";
this.sbProgressBar.Size = new System.Drawing.Size(100, 18);
this.sbProgressBar.Tag = "ProgressBar";
this.sbProgressBar.ToolTipText = "Percentuale di lavoro";
//
// sbStatusLabel
//
this.sbStatusLabel.AutoSize = false;
this.sbStatusLabel.BorderSides = ((System.Windows.Forms.ToolStripStatusLabelBorderSides)((((System.Windows.Forms.ToolStripStatusLabelBorderSides.Left | System.Windows.Forms.ToolStripStatusLabelBorderSides.Top)
| System.Windows.Forms.ToolStripStatusLabelBorderSides.Right)
| System.Windows.Forms.ToolStripStatusLabelBorderSides.Bottom)));
this.sbStatusLabel.BorderStyle = System.Windows.Forms.Border3DStyle.SunkenInner;
this.sbStatusLabel.Name = "sbStatusLabel";
this.sbStatusLabel.Size = new System.Drawing.Size(100, 19);
this.sbStatusLabel.Tag = "Label";
this.sbStatusLabel.ToolTipText = "Stato di eventuali operazioni in background";
//
// btnTest1
//
this.btnTest1.Location = new System.Drawing.Point(11, 5);
this.btnTest1.Name = "btnTest1";
this.btnTest1.Size = new System.Drawing.Size(99, 32);
this.btnTest1.TabIndex = 4;
this.btnTest1.Text = "Serializza";
this.btnTest1.UseVisualStyleBackColor = true;
this.btnTest1.Click += new System.EventHandler(this.btnTest1_Click);
//
// pnlTop
//
this.pnlTop.Controls.Add(this.btnTest5);
this.pnlTop.Controls.Add(this.btnTest4);
this.pnlTop.Controls.Add(this.btnTest3);
this.pnlTop.Controls.Add(this.btnTest2);
this.pnlTop.Controls.Add(this.btnTest1);
this.pnlTop.Dock = System.Windows.Forms.DockStyle.Top;
this.pnlTop.Location = new System.Drawing.Point(0, 0);
this.pnlTop.Name = "pnlTop";
this.pnlTop.Size = new System.Drawing.Size(700, 42);
this.pnlTop.TabIndex = 0;
//
// btnTest2
//
this.btnTest2.Location = new System.Drawing.Point(113, 5);
this.btnTest2.Name = "btnTest2";
this.btnTest2.Size = new System.Drawing.Size(99, 32);
this.btnTest2.TabIndex = 5;
this.btnTest2.Text = "Deserializza";
this.btnTest2.UseVisualStyleBackColor = true;
this.btnTest2.Click += new System.EventHandler(this.btnTest2_Click);
//
// btnTest4
//
this.btnTest4.Location = new System.Drawing.Point(337, 5);
this.btnTest4.Name = "btnTest4";
this.btnTest4.Size = new System.Drawing.Size(99, 32);
this.btnTest4.TabIndex = 7;
this.btnTest4.Text = "Deserializza ALL";
this.btnTest4.UseVisualStyleBackColor = true;
this.btnTest4.Click += new System.EventHandler(this.btnTest4_Click);
//
// btnTest3
//
this.btnTest3.Location = new System.Drawing.Point(235, 5);
this.btnTest3.Name = "btnTest3";
this.btnTest3.Size = new System.Drawing.Size(99, 32);
this.btnTest3.TabIndex = 6;
this.btnTest3.Text = "Serializza ALL";
this.btnTest3.UseVisualStyleBackColor = true;
this.btnTest3.Click += new System.EventHandler(this.btnTest3_Click);
//
// btnTest5
//
this.btnTest5.Location = new System.Drawing.Point(459, 5);
this.btnTest5.Name = "btnTest5";
this.btnTest5.Size = new System.Drawing.Size(99, 32);
this.btnTest5.TabIndex = 9;
this.btnTest5.Text = "Import Milu";
this.btnTest5.UseVisualStyleBackColor = true;
this.btnTest5.Click += new System.EventHandler(this.btnTest5_Click);
//
// frmReader
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(700, 444);
this.ControlBox = false;
this.Controls.Add(this.rtb);
this.Controls.Add(this.pnlTop);
this.Controls.Add(this.sb);
this.Name = "frmReader";
this.Text = "PlugIn_App_Reader.UI Reader (DB vrs1.3, App" +
" vrsX.X.X.X)";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.frmReader_FormClosing);
this.sb.ResumeLayout(false);
this.sb.PerformLayout();
this.pnlTop.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.RichTextBox rtb;
private System.Windows.Forms.StatusStrip sb;
private System.Windows.Forms.ToolStripStatusLabel sbMessage;
private System.Windows.Forms.ToolStripProgressBar sbProgressBar;
private System.Windows.Forms.ToolStripStatusLabel sbStatusLabel;
private System.Windows.Forms.Button btnTest1;
private System.Windows.Forms.Panel pnlTop;
private System.Windows.Forms.Button btnTest2;
private System.Windows.Forms.Button btnTest4;
private System.Windows.Forms.Button btnTest3;
private System.Windows.Forms.Button btnTest5;
}
}
+811
View File
@@ -0,0 +1,811 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using A69WD.Data;
using A69WD.Net;
using A69WD.Service;
namespace A69WD.UI {
public partial class frmReader : Form {
MenuItem mSkrStart;
MenuItem mSkrStop;
MenuItem mImpStart;
MenuItem mImpStop;
private Service_Crawler serviceCrawler = null;
private Service_Importer ServiceImporter = null;
public frmReader() {
InitializeComponent();
// Imposto caption:
this.Text = String.Format("A69WD - Reader Vrs {0}", cTools.AssemblyVersion);
// Imposto field interni:
nav = new Navigator(25);
// Visualizzo la pagina iniziale di riepilogo dell'archivio
rtb.Text = nav.Home;
// Aggiorno la status bar
sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// Menu comandi skraping:
MenuItem mSkr = new MenuItem("Skraping");
mSkrStart = new MenuItem("Start", MenuItem_Skraping_Start_Click, Shortcut.None);
mSkrStop = new MenuItem("Stop", MenuItem_Skraping_Stop_Click, Shortcut.None);
mSkr.MenuItems.Add(mSkrStart);
mSkr.MenuItems.Add(mSkrStop);
// Menu comandi importer:
MenuItem mImp = new MenuItem("Importer");
mImpStart = new MenuItem("Start", MenuItem_Importer_Start_Click, Shortcut.None);
mImpStop = new MenuItem("Stop", MenuItem_Importer_Stop_Click, Shortcut.None);
mImp.MenuItems.Add(mImpStart);
mImp.MenuItems.Add(mImpStop);
// Menu contestuale:
this.ContextMenu = new ContextMenu();
this.ContextMenu.MenuItems.Add(new MenuItem("View Log", MenuItem_ShowLog_Click, Shortcut.None));
this.ContextMenu.MenuItems.Add(mSkr);
this.ContextMenu.MenuItems.Add(mImp);
// Enable only start
mSkrStart.Enabled = true;
mSkrStop.Enabled = false;
mImpStart.Enabled = true;
mImpStop.Enabled = false;
// TEST // Update UI
// TEST pnlTop.Enabled = false;
// TEST pnlTop.Visible = false;
}
/// <summary>
/// Gestione della navigazione fra i racconti
/// </summary>
private Navigator nav = null;
private void KeyDownAnalisi(object sender, KeyEventArgs e) {
#region Keys.F1 --> Show Help
//if (e.KeyCode == Keys.F1) {
// sbMessage.Text = "Show Help";
// MessageBox.Show(this, Help(), "Help", MessageBoxButtons.OK);
// return;
//}
#endregion
#region Keys.ESC --> Hide Reader
if (e.KeyCode == Keys.Escape) {
//this.Hide();
this.Close();
return;
}
#endregion
#region Keys.F2 --> Show Rac by ID
//if (e.KeyCode == Keys.F2) {
// // Form per richiedere l'ID di un racconto specifico da visualizzare:
// frmRqId frmi = new frmRqId();
// frmi.Left = Left + (Width - frmi.Width) / 2;
// frmi.Top = Top + (Height - frmi.Height) / 2;
// // Visualizzo il racconto richiesto:
// if (frmi.ShowDialog() == DialogResult.OK) {
// rtb.Text = nav.SwitchTo(frmi.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// }
// frmi.Close();
// return;
//}
#endregion
#region Keys.F3 --> Show Rac random
if (e.KeyCode == Keys.F3) {
// Show New Random Rac
rtb.Text = nav.RandomRac;
sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
return;
}
#endregion
#region Keys.F4 --> Genera un racconto nuovo
if (e.KeyCode == Keys.F4) {
// Genera un racconto nuovo
StringBuilder sb = new StringBuilder();
sb.AppendLine("- ID Rac: 0");
sb.AppendLine("- Title: Titolo del nuovo racconto");
sb.AppendLine("- SubTit: ");
sb.AppendLine("- Autors: Mrk");
sb.AppendLine("- Voto: Non Letto");
sb.AppendLine("- Tags: Uncategorized");
sb.AppendLine("- Notes: SrcFile -|- Generated Internally");
sb.AppendLine(string.Format("- Creato: {0}", DateTime.Now.ToString("yyyy/MM/dd")));
sb.AppendLine(string.Format("- Update: {0}", DateTime.Now.ToString("yyyy/MM/dd")));
sb.AppendLine("- Origin: Mrk");
sb.AppendLine("- Trama: Trama del nuovo racconto");
rtb.Text = sb.ToString();
System.Windows.Forms.Clipboard.SetText(sb.ToString());
sbMessage.Text = string.Format("Generato un racconto nuovo, salvarlo con F8");
return;
}
#endregion
#region Keys.F5 --> Refresh Rac
if (e.KeyCode == Keys.F5) {
rtb.Text = nav.Refresh;
sbMessage.Text = string.Format("Racconto {0} Refresh", nav.ActualID.ToString("00000"));
return;
}
#endregion
#region Keys.F8 --> Edit Rac
if (e.KeyCode == Keys.F8) {
// Edit Rac
// Rileggo il testo che visualizzo (e che ho eventualmente editato) per recuperare i testi in esso e li salvo
// Se ID=0 o non specificato, aggiungo il testo come nuovo, altrimenti faccio l'update del racconto.
cRac[] racs = null;
int res = cRac.Read(rtb.Text, out racs);
if (res > 0) {
rtb.Text = string.Format("Trovati {0} racconti:\r\n", racs.Length);
res = 0;
foreach (cRac rac in racs) {
// Aggiorno il racconto in memoria
res += App.DB.UpdateRac(rac);
}
if (res == racs.Length) {
rtb.Text = nav.SwitchTo(racs[0].IDrac);
sbMessage.Text = string.Format("Importato/Aggiornato correttamente {0} racconti", res.ToString("00000"));
} else {
rtb.Text = App.DB.GetRacById(nav.ActualID).ToString();
sbMessage.Text = string.Format("Errore in lettura di {0}", nav.ActualID.ToString("00000"));
}
} else {
rtb.Text = "Lettura del racconto non riuscita";
}
return;
}
#endregion
#region Keys.F10 --> Bookmarks
if (e.KeyCode == Keys.F10) {
// ToDo: Keys.F10 --> Bookmarks
sbMessage.Text = "Keys.F10 --> Comando in sviluppo, non tutte le funzioni sono completate";
return;
}
#endregion
#region Keys.F12 --> Votazione
//if (e.KeyCode == Keys.F12 && (!e.Control) && (!e.Alt) && (!e.Shift)) {
// // Vote Rac
// long id = nav.ActualID;
// if (id < 0) return;
// frmVoto frmv = new frmVoto(App.DB.GetRacById(id));
// frmv.Left = Left + (Width - frmv.Width) / 2;
// frmv.Top = Top + (Height - frmv.Height) / 2;
// if (frmv.ShowDialog() == DialogResult.OK) {
// rtb.Text = nav.UpdateVoto(frmv.Voto);
// sbMessage.Text = string.Format("Racconto {0} Voted", nav.ActualID.ToString("00000"));
// } else {
// rtb.Text = nav.Refresh;
// sbMessage.Text = string.Format("Racconto {0} Refresh", nav.ActualID.ToString("00000"));
// }
// frmv.Close();
// return;
//}
#endregion
#region Keys.Alt+keys.H --> Home
if (e.Alt && (e.KeyCode == Keys.H)) {
rtb.Text = nav.Home;
sbMessage.Text = string.Format("Home {0}", nav.ActualID.ToString("00000"));
}
#endregion
#region Keys.Alt+Keys.Left --> Navigazione backward fra i racconti già visualizzati
if (e.Alt && (e.KeyCode == Keys.Left)) {
// Show Rac Backward
rtb.Text = nav.Backward;
sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
return;
}
#endregion
#region Keys.Alt+keys.Right --> Navigazione forward fra i racconti già visualizzati
if (e.Alt && (e.KeyCode == Keys.Right)) {
// Show Rac Forward
rtb.Text = nav.Forward;
sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
return;
}
#endregion
#region Keys.Alt+keys.Down --> Navigazione verso il racconto con ID precedente (ID Meno 1) a quello visualizzato
if (e.Alt && (e.KeyCode == Keys.Down)) {
// Show Rac Meno 1
rtb.Text = nav.Previous;
sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
return;
}
#endregion
#region Keys.Alt+keys.Up --> Navigazione verso il racconto con ID successivo (ID Più 1) a quello visualizzato
if (e.Alt && (e.KeyCode == Keys.Up)) {
// Show Rac Più 1
rtb.Text = nav.Next;
sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
return;
}
#endregion
#region Keys.Alt+keys.+ --> Incrementa la dimensione del font
if (e.Alt && (e.KeyCode == Keys.Add)) {
// Increse text size
Font f = new System.Drawing.Font(rtb.Font.FontFamily, rtb.Font.Size + 1.0F);
rtb.Font = f;
sbMessage.Text = string.Format("Racconto {0} Size Increase", nav.ActualID.ToString("00000"));
return;
}
#endregion
#region Keys.Alt+keys.- --> Decrementa la dimensione del font
if (e.Alt && (e.KeyCode == Keys.Subtract)) {
// Decrease text size
Font f = new System.Drawing.Font(rtb.Font.FontFamily, rtb.Font.Size - 1.0F);
rtb.Font = f;
sbMessage.Text = string.Format("Racconto {0} Size Decrease", nav.ActualID.ToString("00000"));
return;
}
#endregion
#region Keys.Alt+keys.* --> Imposta il font alla dimensione di default
if (e.Alt && (e.KeyCode == Keys.Multiply)) {
// Reset text size
Font f = new System.Drawing.Font(rtb.Font.FontFamily, (float)10.0);
rtb.Font = f;
sbMessage.Text = string.Format("Racconto {0} Size Default", nav.ActualID.ToString("00000"));
return;
}
#endregion
#region Keys.Alt+keys.F --> Editazione filtri:
//if (e.Alt && (e.KeyCode == Keys.F)) {
// // preparo l'interfaccia per la gestione filtri:
// frmFilters frmF = new frmFilters(DB);
// frmF.Left = Left + (Width - frmF.Width) / 2;
// frmF.Top = Top + (Height - frmF.Height) / 2;
// // Visualizzo l'interfaccia:
// if (frmF.ShowDialog() == DialogResult.OK) { sbMessage.Text = string.Format("FVoti: {0} - FTags: {1}", DB.FVoti, DB.FTags); }
// frmF.Close();
// return;
//}
#endregion
#region Keys.Alt+keys.A --> Navigazione fra i racconti dello stesso autore
//if (e.Alt && (e.KeyCode == Keys.A)) {
// // Show All Rac of this Autor
// frmList frml = new frmList();
// frml.Height = this.Height - 64;
// frml.Width = this.Width - 64;
// frml.Left = Left + (Width - frml.Width) / 2;
// frml.Top = Top + (Height - frml.Height) / 2;
// frml.Title = "Racconti dell'autore:";
// int res = DB.GetListByRacAutor(nav.ActualID, frml.LV);
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// // Se racconto selezionato è uno solo, lo visualizzo, altrimenti mostro nuovo elenco
// if (frml.Num > 1) {
// // più racconti dallo stesso titolo:
// res = DB.GetListByTitle(frml.Descr, frml.LV);
// frml.Title = "Racconti con il titolo: " + frml.Descr;
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// }
// } else {
// // 1 solo racconto
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// }
// }
// frml.Close();
// return;
//}
#endregion
#region Keys.Alt+keys.T --> Navigazione fra i racconti via tag:
//if (e.Alt && (e.KeyCode == Keys.T)) {
// // Show All Tag
// frmList frml = new frmList();
// frml.Height = this.Height - 64;
// frml.Width = this.Width - 64;
// frml.Left = Left + (Width - frml.Width) / 2;
// frml.Top = Top + (Height - frml.Height) / 2;
// frml.Title = "Elenco Tags:";
// int res = DB.GetListTags(nav.ActualID, frml.LV);
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// // Show all rac del tag selezionato
// res = DB.GetListByTags(frml.ID, frml.LV);
// frml.Title = "Racconti del tag: " + frml.Descr;
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// // Se racconto selezionato è uno solo, lo visualizzo, altrimenti mostro nuovo elenco
// if (frml.Num > 1) {
// // più racconti dallo stesso titolo:
// res = DB.GetListByTitle(frml.Descr, frml.LV);
// frml.Title = "Racconti con il titolo: " + frml.Descr;
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// }
// } else {
// // 1 solo racconto
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// }
// }
// }
// frml.Close();
// return;
//}
#endregion
#region Keys.Alt+keys.V --> Navigazione fra i racconti via Voto:
//if (e.Alt && (e.KeyCode == Keys.V)) {
// // Show All Voto
// frmList frml = new frmList();
// frml.Height = this.Height - 64;
// frml.Width = this.Width - 64;
// frml.Left = Left + (Width - frml.Width) / 2;
// frml.Top = Top + (Height - frml.Height) / 2;
// frml.Title = "Elenco Voti Assegnati:";
// int res = DB.GetListVoti(nav.ActualID, frml.LV);
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// // Show all rac del tag selezionato
// frml.Title = "Racconti del voto: " + frml.Descr;
// res = DB.GetListByVoto(frml.ID, frml.LV);
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// }
// }
// frml.Close();
// return;
//}
#endregion
#region Keys.Alt+keys.D --> Navigazione fra i racconti via Data:
//if (e.Alt && (!e.Shift) && (e.KeyCode == Keys.D)) {
// // Show All Year
// frmList frml = new frmList();
// frml.Height = this.Height - 64;
// frml.Width = this.Width - 64;
// frml.Left = Left + (Width - frml.Width) / 2;
// frml.Top = Top + (Height - frml.Height) / 2;
// frml.Title = "Elenco per anno di creazione:";
// int res = DB.GetListByCreationDate(frml.LV);
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// if (frml.Num == 1) {
// // Per l'anno vi è 1 solo racconto, e quindi lo mostro direttamente:
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// } else {
// // Show all Month dell'anno selezionato
// string strYear = frml.Descr;
// res = DB.GetListByYear(strYear, frml.LV);
// frml.Title = "Elenco mesi dell'anno " + strYear;
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// if (frml.Num == 1) {
// // Per l'anno e mese selezionato, vi è 1 solo racconto, e quindi lo mostro direttamente:
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// } else {
// // Show all Days del mese, dell'anno, selezionato
// string strMonth = frml.Descr;
// res = DB.GetListByMonth(strYear, strMonth, frml.LV);
// frml.Title = "Elenco giorni del mese " + strMonth + "/" + strYear;
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// if (frml.Num == 1) {
// // solo un racconto nel mese indicato, lo visualizzo subito
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// } else {
// string strDay = frml.Descr;
// res = DB.GetListByDay(strYear, strMonth, strDay, frml.LV);
// frml.Title = "Elenco racconti del giorno " + strDay + "/" + strMonth + "/" + strYear;
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// // Se racconto selezionato è uno solo, lo visualizzo, altrimenti mostro nuovo elenco
// if (frml.Num > 1) {
// // più racconti dallo stesso titolo:
// res = DB.GetListByTitle(frml.Descr, frml.LV);
// frml.Title = "Racconti con il titolo: " + frml.Descr;
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// }
// } else {
// // 1 solo racconto
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// }
// }
// }
// }
// }
// }
// }
// }
// frml.Close();
// return;
//}
#endregion
#region Keys.Alt+keys.L --> Navigazione degli ultimi 25 racconti updated e non letti:
//if (e.Alt && (e.KeyCode == Keys.L)) {
// #region
// // Show last 25 racconti updated:
// frmList frml = new frmList();
// frml.Height = this.Height - 64;
// frml.Width = this.Width - 64;
// frml.Left = Left + (Width - frml.Width) / 2;
// frml.Top = Top + (Height - frml.Height) / 2;
// frml.Title = "Ultimi 25 racconti modificati:";
// int res = DB.GetListByUpdate(50, frml.LV);
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// // Vi è un solo racocnto e lo visualizzo:
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// }
// frml.Close();
// //this.Select();
// return;
// #endregion
//}
#endregion
#region Keys.Alt+keys.I --> Navigazione fra i racconti via iniziale titolo:
//if (e.Alt && (!e.Shift) && (e.KeyCode == Keys.I)) {
// // Show All Iniziali
// frmList frml = new frmList();
// frml.Height = this.Height - 64;
// frml.Width = this.Width - 64;
// frml.Left = Left + (Width - frml.Width) / 2;
// frml.Top = Top + (Height - frml.Height) / 2;
// frml.Title = "Elenco iniziali dei titoli:";
// int res = DB.GetListByIniziali(frml.LV);
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// if (frml.Num == 1) {
// // Per l'iniziale vi è 1 solo racconto, e quindi lo mostro direttamente:
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// } else {
// // Show all titoli con l'iniziale selezionata
// string iniziale = frml.Descr;
// res = DB.GetListByIniziale(iniziale, frml.LV);
// frml.Title = "Elenco titoli con l'iniziale " + iniziale;
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// // Se racconto selezionato è uno solo, lo visualizzo, altrimenti mostro nuovo elenco
// if (frml.Num > 1) {
// // più racconti dallo stesso titolo:
// res = DB.GetListByTitle(frml.Descr, frml.LV);
// frml.Title = "Racconti con il titolo: " + frml.Descr;
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// }
// } else {
// // 1 solo racconto
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// }
// }
// }
// }
// frml.Close();
// return;
//}
#endregion
#region Shift+keys.Alt+keys.D --> Visualizza tutti i racconti non letti con la data attuale:
//if (e.Alt && e.Shift && (e.KeyCode == Keys.D)) {
// // ricavo la data del racconto visualizzato:
// cRac rac = App.DB.GetRacById(nav.ActualID);
// string strYear = rac.Creato.Year.ToString("0000");
// string strMonth = rac.Creato.Month.ToString("00");
// string strDay = rac.Creato.Day.ToString("00");
// // Show tutti i racconti non letti della data indicata:
// frmList frml = new frmList();
// frml.Height = this.Height - 64;
// frml.Width = this.Width - 64;
// frml.Left = Left + (Width - frml.Width) / 2;
// frml.Top = Top + (Height - frml.Height) / 2;
// int res = DB.GetListByDay(strYear, strMonth, strDay, frml.LV);
// frml.Title = "Elenco racconti del giorno " + strDay + "/" + strMonth + "/" + strYear;
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// // Se racconto selezionato è uno solo, lo visualizzo, altrimenti mostro nuovo elenco
// if (frml.Num > 1) {
// // più racconti dallo stesso titolo:
// res = DB.GetListByTitle(frml.Descr, frml.LV);
// frml.Title = "Racconti con il titolo: " + frml.Descr;
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// }
// } else {
// // 1 solo racconto
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// }
// }
// frml.Close();
// return;
//}
#endregion
#region Shift+Keys.Alt+keys.I --> Navigazione fra i racconti con la stessa iniziale del titolo:
//if (e.Alt && e.Shift && (e.KeyCode == Keys.I)) {
// // ricavo L'iniziale del titolo del racocnto visualizzato:
// cRac rac = App.DB.GetRacById(nav.ActualID);
// string iniziale = rac.Title.Substring(0, 1);
// // Show All Iniziali
// frmList frml = new frmList();
// frml.Height = this.Height - 64;
// frml.Width = this.Width - 64;
// frml.Left = Left + (Width - frml.Width) / 2;
// frml.Top = Top + (Height - frml.Height) / 2;
// int res = DB.GetListByIniziale(iniziale, frml.LV);
// frml.Title = "Elenco titoli con l'iniziale " + iniziale;
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// // Se racconto selezionato è uno solo, lo visualizzo, altrimenti mostro nuovo elenco
// if (frml.Num > 1) {
// // più racconti dallo stesso titolo:
// res = DB.GetListByTitle(frml.Descr, frml.LV);
// frml.Title = "Racconti con il titolo: " + frml.Descr;
// if ((res > 0) && (frml.ShowDialog() == DialogResult.OK)) {
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// }
// } else {
// // 1 solo racconto
// rtb.Text = nav.SwitchTo(frml.ID);
// sbMessage.Text = string.Format("Racconto {0}", nav.ActualID.ToString("00000"));
// }
// }
// frml.Close();
// return;
//}
#endregion
#region Shift+keys.Alt+F12 --> Ricrea l'intero DB eliminando i racconti segnati da eliminare
if (e.KeyCode == Keys.F12 && (!e.Control) && e.Alt && e.Shift) {
// ToDo: Shift+Alt+F12 --> Ricrea l'intero DB eliminando i racconti segnati da eliminare
sbMessage.Text = "Shift+Alt+F12 --> Comando in sviluppo, non tutte le funzioni sono completate";
return;
}
#endregion
#region Keys.Alt+Keys.Q --> Esegue una query personalizzata:
if (e.Alt && (e.KeyCode == Keys.Z)) {
// ToDo: Keys.Alt+Keys.Q --> Esegue una query personalizzata
sbMessage.Text = "Keys.Alt+Keys.Q --> Comando in sviluppo, non tutte le funzioni sono completate";
return;
}
#endregion
#region Keys.Alt+keys.Z --> Analisi Racconti Duplicati:
if (e.Alt && (e.KeyCode == Keys.Z)) {
// ToDo: Keys.Alt+keys.Z --> Analisi Racconti Duplicati:
sbMessage.Text = "Shift+Alt+F12 --> Comando in sviluppo, non tutte le funzioni sono completate";
return;
}
#endregion
#region Keys.Alt+keys.S --> Crea uno snapshot di n racconti formattati e li converte in ebook
//if (e.Alt && (e.KeyCode == Keys.S)) {
// // // ToDo: Keys.Alt+keys.S --> Crea uno snapshot di n racconti formattati e li converte in ebook
// // sbMessage.Text = "Keys.Alt+keys.S --> Comando in sviluppo, non tutte le funzioni sono completate";
// // return;
// cmdCreaEbook();
//}
#endregion
#region Keys.Alt+keys.U --> Unione di due racconti
//if (e.Alt && (e.KeyCode == Keys.U)) {
// // ToDo: Keys.Alt+keys.U --> Unione di due racconti
// // - chiede l'ID dei due racconti da unire
// frmIDs frmid = new frmIDs();
// frmid.Left = Left + (Width - frmid.Width) / 2;
// frmid.Top = Top + (Height - frmid.Height) / 2;
// if (frmid.ShowDialog() != DialogResult.OK) {
// sbMessage.Text = "Anullato inserimento degli ID dei racconti da unire";
// return;
// }
// if ((frmid.IDleft <= 0) || (frmid.IDright <= 0)) {
// sbMessage.Text = "ID dei racconti da unire non validi";
// return;
// }
// // - ricavo i due racconti da unire:
// cRac left = DB.GetRacById(frmid.IDleft);
// cRac right = DB.GetRacById(frmid.IDright);
// frmid.Close();
// // - visualizza il form di unione
// frmUnion frm = new frmUnion(left, right, DB);
// if (frm.ShowDialog() == DialogResult.OK) {
// // - Left: il racconto unione:
// left = frm.Unione;
// // - Right: lo indico come duplicato:
// right.Title = "Doppione";
// right.SubTitle = left.strIDrac;
// right.Voto = "Da Cancellare";
// right.Tags.Clear();
// right.Tags.Add("Doppione");
// right.Notes.Clear();
// right.Notes.Add(new cNota("SrcFile", "Doppione"));
// right.Trama = "Racconto duplicato del " + left.strIDrac;
// // - Aggiorno il DB
// if ((right.IsValid) && (left.IsValid)) {
// App.DB.UpdateRac(right);
// App.DB.UpdateRac(left);
// }
// }
// frm.Close();
// return;
//}
#endregion
#region
#endregion
}
private void frmReader_FormClosing(object sender, FormClosingEventArgs e) {
// Prima di chiudere, devo fermare l'importazione o lo scraping
if (serviceCrawler != null) { serviceCrawler.StopWorker(); }
if (ServiceImporter != null) { ServiceImporter.StopWorker(); }
}
async void MenuItem_Skraping_Start_Click(object sender, EventArgs e) {
// Update menu
mSkrStart.Enabled = false;
mSkrStop.Enabled = true;
mImpStart.Enabled = false;
mImpStop.Enabled = false;
// - Verifico path per il download
if (!System.IO.Directory.Exists(Cfg.Settings.PathDownloadWorking)) System.IO.Directory.CreateDirectory(Cfg.Settings.PathDownloadWorking);
// Preparo il servizio
serviceCrawler = new Service_Crawler(sb);
// Inserisco i link iniziali nella coda
if (!cLink_eRac.InsertStartLink(serviceCrawler)) {
Logger.Warning("eRac - Insert start link Fault");
sbStatusLabel.Text = "eRac fault.";
return;
}
if (!cLink_A69.InsertStartLink(serviceCrawler)) {
Logger.Warning("A69 - Insert start link Fault");
sbStatusLabel.Text = "A69 fault.";
return;
}
// avvio il servizio
serviceCrawler.StartWorker();
}
void MenuItem_Skraping_Stop_Click(object sender, EventArgs e) {
// Update menu
mSkrStart.Enabled = false;
mSkrStop.Enabled = false;
mImpStart.Enabled = true;
mImpStop.Enabled = false;
if (serviceCrawler != null) { serviceCrawler.StopWorker(); }
}
void MenuItem_Importer_Start_Click(object sender, EventArgs e) {
// Update menu
mSkrStart.Enabled = false;
mSkrStop.Enabled = false;
mImpStart.Enabled = false;
mImpStop.Enabled = true;
// Preparo il servizio
ServiceImporter = new Service_Importer(sb);
// Per ora uso la directory di default, poi metteremo selezione
string path = Cfg.Settings.PathImport;
// avvio il servizio
ServiceImporter.StartWorker(path);
}
void MenuItem_Importer_Stop_Click(object sender, EventArgs e) {
// Update menu
mSkrStart.Enabled = false;
mSkrStop.Enabled = false;
mImpStart.Enabled = false;
mImpStop.Enabled = false;
if (ServiceImporter != null) { ServiceImporter.StopWorker(); }
}
void MenuItem_ShowLog_Click(object sender, EventArgs e) {
cTools.ShowOnNotepad(Cfg.Settings.PathFileLogs);
}
private void btnTest1_Click(object sender, EventArgs e) {
// Test serializzazione su file xml
// - Verifico path per il download
if (!System.IO.Directory.Exists(Cfg.Settings.PathDownloadWorking)) System.IO.Directory.CreateDirectory(Cfg.Settings.PathDownloadWorking);
// Serializzo 5 racconti casuali
for (int i = 0; i < 5; i++) {
cRac rac = App.DB.GetRacById(App.DB.GetRandomIDrac());
string path = System.IO.Path.Combine(Cfg.Settings.PathDownloadWorking, $"ATDB_Rac_{rac.strIDrac}.xml");
rac.SaveToXmlFile(path);
}
}
private void btnTest2_Click(object sender, EventArgs e) {
// Test deserializzazione da file xml
string[] files = System.IO.Directory.GetFiles(Cfg.Settings.PathImport, "ATDB_*.xml");
foreach (string file in files) {
cRac rac = cRac.LoadFromXmlFile(file);
var res = App.DB.ImportRac(rac);
}
}
private void btnTest3_Click(object sender, EventArgs e) {
// Test serializzazione su file xml di tutti i racconti:
// - Verifico path per il download
if (!System.IO.Directory.Exists(Cfg.Settings.PathDownloadWorking)) System.IO.Directory.CreateDirectory(Cfg.Settings.PathDownloadWorking);
// - Ricavo il numero totale di racconti:
long nRac = App.DB.GetNumRacs();
// - Serializzo i racconti
for (long i = 0; i <= nRac; i++) {
cRac rac = App.DB.GetRacById(i);
if (rac == null) continue;
string path = System.IO.Path.Combine(Cfg.Settings.PathDownloadWorking, $"ATDB_Rac_{rac.strIDrac}.xml");
rac.SaveToXmlFile(path);
}
}
private void btnTest4_Click(object sender, EventArgs e) {
// Test deserializzazione da file xml
string[] files = System.IO.Directory.GetFiles(Cfg.Settings.PathImport, "ATDB_*.xml");
foreach (string file in files) {
cRac rac = cRac.LoadFromXmlFile(file);
var res = App.DB.ImportRac(rac);
}
}
private void btnTest5_Click(object sender, EventArgs e) {
// Test importer dal vecchio formato di Milu:
// - Preparo il servizio
ServiceImportMilu = new Service_Import_Milu(sb);
// - Per ora uso la directory di default, poi metteremo selezione
string path = Cfg.Settings.PathImport;
// avvio il servizio
ServiceImportMilu.StartWorker(path);
}
Service_Import_Milu ServiceImportMilu = null;
}
}
@@ -117,7 +117,7 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="openFileDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<metadata name="sb.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>