Zautomatyzuj swoją bazę notatek z Filmików, YouTuba, Newsletterów i artykułów

Cezary Walenciuk

Zautomatyzuj
swoją bazę notatek z Filmików, YouTuba Newsletterów
i artykułów

@walenciukC

Speaker
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW

Windows Service in Python

                    




























                        import os
                        import time
                        import servicemanager
                        import win32service
                        import win32serviceutil
                        import win32event
                        from datetime import datetime

                        class HourlyFileService(win32serviceutil.ServiceFramework):
                        _svc_name_ = "HourlyFileService"
                        _svc_display_name_ = "Hourly File Creation Service"
                        _svc_description_ = "Tworzy nowy plik z timestampem co godzinę."

                        def __init__(self, args):
                            super().__init__(args)
                            # Zdarzenie do obsługi zatrzymania usługi:
                            self.stop_event = win32event.CreateEvent(None, 0, 0, None)
                            self.running = True

                        def SvcStop(self):
                            # Wywoływane przy zatrzymaniu usługi
                            self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
                            self.running = False
                            win32event.SetEvent(self.stop_event)

                        def SvcDoRun(self):
                            servicemanager.LogInfoMsg("HourlyFileService – start")
                            # Pętla główna usługi
                            while self.running:
                                try:
                                    # Tworzenie pliku w katalogu usługi
                                    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
                                    filename = f"plik_{timestamp}.txt"
                                    # np. w katalogu C:\HourlyFiles
                                    output_dir = r"C:\Notes\HourlyFiles"
                                    os.makedirs(output_dir, exist_ok=True)
                                    full_path = os.path.join(output_dir, filename)
                                    with open(full_path, "w") as f:
                                        f.write(f"Plik utworzony: {timestamp}\n")
                                    servicemanager.LogInfoMsg(f"Utworzono plik: {full_path}")
                                except Exception as e:
                                    servicemanager.LogErrorMsg(f"Błąd przy tworzeniu pliku: {e}")

                                # Czekaj do 3600 sekund lub do sygnału zatrzymania
                                win32event.WaitForSingleObject(self.stop_event, 3600 * 1000)

                            servicemanager.LogInfoMsg("HourlyFileService – stop")

                        if __name__ == '__main__':
                        win32serviceutil.HandleCommandLine(HourlyFileService)
                    
                

Windows Service in Python

                    
                        python HourlyFileService.py install

                        python HourlyFileService.py start

                        python HourlyFileService.py stop
                        python HourlyFileService.py remove
                    
                
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW

Pobieranie kanałów ID z adresów przy użyciu YouTube API w Python

                    









                        import os
                        from googleapiclient.discovery import build

                        def get_channel_id(api_key, url):
                            youtube = build('youtube', 'v3', developerKey=api_key)
                            if 'youtube.com/watch' in url:
                                # Wyciągnij videoId z URL-a
                                video_id = url.split('v=')[1].split('&')[0]
                                response = youtube.videos().list(part='snippet', id=video_id).execute()
                                return response['items'][0]['snippet']['channelId']

                            elif 'youtube.com/channel/' in url:
                                # Bezpośredni ID kanału
                                return url.rstrip('/').split('/')[-1]

                            elif ('youtube.com/c/' in url) or ('youtube.com/user/' in url) or ('youtube.com/@' in url):
                                # Wyciągnij nazwę kanału i znajdź ID przez wyszukiwanie
                                channel_name = url.rstrip('/').split('/')[-1]
                                response = youtube.search().list(
                                    part='id',
                                    q=channel_name,
                                    type='channel',
                                    maxResults=1
                                ).execute()
                                return response['items'][0]['id']['channelId']

                            else:
                                raise ValueError("Nieprawidłowy URL YouTube")

                        # Example usage
                        api_key = os.getenv('YOUTUBE_API_KEY')   # Replace with your actual API key
                        url = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'  # Example video URL
                        channel_id = get_channel_id(api_key, url)
                        print(f"Channel ID: {channel_id}")


                    
                
blog
CW

Pobieranie listy najnowszych filmików przy pomocy YouTube API w C# = 1

                    












                        try
                        {
                            var youtubeService = new YTHelperService();
                            string youtubeUrl = "https://www.youtube.com/@nickchapsas";

                            Console.WriteLine($"Pobieranie Channel ID dla: {youtubeUrl}");

                            // Krok 1: Znajdź Channel ID na podstawie URL
                            string channelId = await youtubeService.GetChannelIdFromUrl(youtubeUrl);

                            if (string.IsNullOrEmpty(channelId))
                            {
                                Console.WriteLine("Nie udało się znaleźć Channel ID");
                                return;
                            }

                            Console.WriteLine($"Znaleziony Channel ID: {channelId}");
                            Console.WriteLine();

                            // Krok 2: Pobierz ostatnie filmiki
                            Console.WriteLine("Pobieranie ostatnich filmików...");
                            var videos = await youtubeService.GetLatestVideos(channelId);

                            // Krok 3: Wyświetl listę filmików
                            youtubeService.DisplayVideos(videos);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"Wystąpił błąd: {ex.Message}");
                        }

                        Console.WriteLine("\nNaciśnij dowolny klawisz aby zakończyć...");
                        Console.ReadKey();
                    
                

Pobieranie listy najnowszych filmików przy pomocy YouTube API w C# = 2

                    


















                        /// <summary>
                        /// Model reprezentujący informacje o filmiku YouTube
                        /// </summary>
                        public class VideoInfo
                        {
                            /// <summary>
                            /// Unikalny identyfikator filmu
                            /// </summary>
                            public string Id { get; set; }

                            /// <summary>
                            /// Pełny URL do filmu
                            /// </summary>
                            public string Url { get; set; }

                            /// <summary>
                            /// Tytuł filmu
                            /// </summary>
                            public string Title { get; set; }

                            /// <summary>
                            /// Opis filmu
                            /// </summary>
                            public string Description { get; set; }

                            /// <summary>
                            /// Data publikacji filmu
                            /// </summary>
                            public string PublishedAt { get; set; }

                            /// <summary>
                            /// Nazwa kanału
                            /// </summary>
                            public string ChannelTitle { get; set; }
                        }
                    
                

Pobieranie listy najnowszych filmików przy pomocy YouTube API w C# = 3

                    






























                        /// <summary>
                        /// Klasa do zarządzania konfiguracją aplikacji
                        /// </summary>
                        public class ConfigurationManager
                        {
                            private readonly IConfiguration _configuration;

                            public ConfigurationManager()
                            {
                                var builder = new ConfigurationBuilder()
                                    .SetBasePath(Directory.GetCurrentDirectory())
                                    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                                    .AddEnvironmentVariables();

                                _configuration = builder.Build();
                            }

                            /// <summary>
                            /// Klucz API YouTube
                            /// </summary>
                            public string ApiKey => Environment.GetEnvironmentVariable("YOUTUBE_API_KEY");

                            /// <summary>
                            /// Nazwa aplikacji
                            /// </summary>
                            public string ApplicationName => GetConfigValue("YouTube:ApplicationName");

                            /// <summary>
                            /// Maksymalna liczba wyników do pobrania
                            /// </summary>
                            public long MaxResults => long.Parse(GetConfigValue("YouTube:MaxResults", "10"));

                            /// <summary>
                            /// Pobierz wartość z konfiguracji
                            /// </summary>
                            /// <param name="key">Klucz konfiguracji</param>
                            /// <param name="defaultValue">Wartość domyślna</param>
                            /// <returns>Wartość konfiguracji</returns>
                            private string GetConfigValue(string key, string defaultValue = null)
                            {
                                var value = _configuration[key];

                                if (string.IsNullOrEmpty(value))
                                {
                                    if (defaultValue != null)
                                        return defaultValue;

                                    throw new InvalidOperationException($"Brak konfiguracji dla klucza: {key}");
                                }

                                return value;
                            }
                        }
                    
                

Pobieranie listy najnowszych filmików przy pomocy YouTube API w C# = 5

                    































































































                        

























































                        public class YTHelperService
                        {
                            private readonly YouTubeService _youtubeService;
                            private readonly ConfigurationManager _config;

                            public YTHelperService()
                            {
                                _config = new ConfigurationManager();
                                _youtubeService = new YouTubeService(new BaseClientService.Initializer() { ApiKey = _config.ApiKey });
                            }

                            /// <summary>
                            /// Znajdź Channel ID na podstawie URL YouTube
                            /// </summary>
                            public async Task<string> GetChannelIdFromUrl(string url)
                            {
                                try
                                {
                                    // Sposób 1: Sprawdź czy to bezpośredni link do kanału z ID
                                    var channelIdMatch = Regex.Match(url, @"youtube\.com/channel/([a-zA-Z0-9_-]+)");
                                    if (channelIdMatch.Success)
                                    {
                                        return channelIdMatch.Groups[1].Value;
                                    }

                                    // Sposób 2: Sprawdź czy to link do filmu - wyciągnij Channel ID z filmu
                                    var videoIdMatch = Regex.Match(url, @"(?:youtube\.com/watch\?v=|youtu\.be/)([a-zA-Z0-9_-]+)");
                                    if (videoIdMatch.Success)
                                    {
                                        string videoId = videoIdMatch.Groups[1].Value;
                                        return await GetChannelIdFromVideoId(videoId);
                                    }

                                    // Sposób 3: Sprawdź czy to link z nazwą użytkownika (@username lub /user/)
                                    var usernameMatch = Regex.Match(url, @"youtube\.com/@([a-zA-Z0-9_-]+)");
                                    if (usernameMatch.Success)
                                    {
                                        string username = usernameMatch.Groups[1].Value;
                                        return await GetChannelIdByUsername(username);
                                    }

                                    var userMatch = Regex.Match(url, @"youtube\.com/user/([a-zA-Z0-9_-]+)");
                                    if (userMatch.Success)
                                    {
                                        string username = userMatch.Groups[1].Value;
                                        return await GetChannelIdByUsername(username);
                                    }

                                    // Sposób 4: Sprawdź czy to link z /c/
                                    var customMatch = Regex.Match(url, @"youtube\.com/c/([a-zA-Z0-9_-]+)");
                                    if (customMatch.Success)
                                    {
                                        string customName = customMatch.Groups[1].Value;
                                        return await GetChannelIdByUsername(customName);
                                    }

                                    throw new ArgumentException("Nierozpoznany format URL YouTube");
                                }
                                catch (Exception ex)
                                {
                                    Console.WriteLine($"Błąd podczas pobierania Channel ID: {ex.Message}");
                                    return null;
                                }
                            }

                            /// <summary>
                            /// Pobierz Channel ID na podstawie Video ID
                            /// </summary>
                            private async Task<string> GetChannelIdFromVideoId(string videoId)
                            {
                                var videoRequest = _youtubeService.Videos.List("snippet");
                                videoRequest.Id = videoId;

                                var videoResponse = await videoRequest.ExecuteAsync();
                                var video = videoResponse.Items.FirstOrDefault();

                                return video?.Snippet.ChannelId;
                            }

                            /// <summary>
                            /// Pobierz Channel ID na podstawie nazwy użytkownika (wyszukiwanie)
                            /// </summary>
                            private async Task<string> GetChannelIdByUsername(string username)
                            {
                                var searchRequest = _youtubeService.Search.List("snippet");
                                searchRequest.Q = username;
                                searchRequest.Type = "channel";
                                searchRequest.MaxResults = 1;

                                var searchResponse = await searchRequest.ExecuteAsync();
                                var channel = searchResponse.Items.FirstOrDefault();

                                return channel?.Snippet.ChannelId;
                            }

                            /// <summary>
                            /// Pobierz ostatnie filmiki z danego kanału
                            /// </summary>
                            public async Task<List<VideoInfo>> GetLatestVideos(string channelId)
                            {
                                try
                                {
                                    var searchRequest = _youtubeService.Search.List("snippet");
                                    searchRequest.ChannelId = channelId;
                                    searchRequest.Type = "video";
                                    searchRequest.Order = SearchResource.ListRequest.OrderEnum.Date;
                                    searchRequest.MaxResults = _config.MaxResults;

                                    var searchResponse = await searchRequest.ExecuteAsync();
                                    var videos = new List<VideoInfo>();

                                    foreach (var item in searchResponse.Items)
                                    {
                                        var video = new VideoInfo
                                        {
                                            Id = item.Id.VideoId,
                                            Url = $"https://www.youtube.com/watch?v={item.Id.VideoId}",
                                            Title = item.Snippet.Title,
                                            Description = item.Snippet.Description,
                                            PublishedAt = item.Snippet.PublishedAt?.ToString("dd.MM.yyyy HH:mm") ?? "Brak daty",
                                            ChannelTitle = item.Snippet.ChannelTitle
                                        };

                                        videos.Add(video);
                                    }

                                    return videos;
                                }
                                catch (Exception ex)
                                {
                                    Console.WriteLine($"Błąd podczas pobierania filmików: {ex.Message}");
                                    return new List<VideoInfo>();
                                }
                            }

                            /// <summary>
                            /// Wyświetl listę filmików w konsoli
                            /// </summary>
                            public void DisplayVideos(List<VideoInfo> videos)
                            {
                                if (videos == null || !videos.Any())
                                {
                                    Console.WriteLine("Brak filmików do wyświetlenia.");
                                    return;
                                }

                                Console.WriteLine($"Znaleziono {videos.Count} filmików:\n");
                                Console.WriteLine(new string('=', 100));

                                for (int i = 0; i < videos.Count; i++)
                                {
                                    var video = videos[i];

                                    Console.WriteLine($"#{i + 1}");
                                    Console.WriteLine($"Tytuł: {video.Title}");
                                    Console.WriteLine($"URL: {video.Url}");
                                    Console.WriteLine($"ID: {video.Id}");
                                    Console.WriteLine($"Kanał: {video.ChannelTitle}");
                                    Console.WriteLine($"Data publikacji: {video.PublishedAt}");
                                    Console.WriteLine($"Opis: {TruncateDescription(video.Description)}");
                                    Console.WriteLine(new string('-', 100));
                                }
                            }

                            /// <summary>
                            /// Skróć opis do określonej długości
                            /// </summary>
                            private string TruncateDescription(string description)
                            {
                                if (string.IsNullOrEmpty(description))
                                    return "Brak opisu";

                                const int maxLength = 200;
                                if (description.Length <= maxLength)
                                    return description;

                                return description.Substring(0, maxLength) + "...";
                            }
                        }
                    
                
blog
CW
blog
CW
blog
CW
blog
CW

Pobieranie filmików YouTube pytubefix

                    


                        #from pytube import YouTube
                        from pytubefix import YouTube
                        from pytubefix.cli import on_progress

                        # Replace with your desired YouTube video URL
                        video_url = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'

                        try:
                        yt = YouTube(video_url,on_progress_callback = on_progress)

                        # Get the highest resolution progressive stream (video + audio)
                        stream = yt.streams.get_highest_resolution()

                        print(f"Downloading: {yt.title}")
                        stream.download(output_path= r"C:\Notes\PyYouTubeTest")
                        print("Download complete!")

                        except Exception as e:
                        print("Error:", e)

                    
                
blog
CW
blog
CW

Pobieranie filmików YouTube yt_dlp

                    
                        import yt_dlp

                        # Replace with your desired YouTube URL
                        video_url = 'https://www.youtube.com/watch?v=2lAe1cqCOXo'

                        # Download options
                        ydl_opts = {
                            'format': 'bestvideo+bestaudio/best',
                            'outtmpl': r"C:\Notes\ytDlpTest\%(title)s.%(ext)s",
                            'noplaylist': True,        # Don't download playlists
                            'quiet': False,            # Show progress
                            'no_warnings': True,       # Suppress warnings
                            'ignoreerrors': True,      # Skip videos that cause errors
                        }

                        # Downloading the video
                        with yt_dlp.YoutubeDL(ydl_opts) as ydl:
                        ydl.download([video_url])
                    
                

Pobieranie filmików YouTube yt_dlp jako MP3

                    








                        import yt_dlp

                        def download_as_mp3(url, output_path='.'):
                            ydl_opts = {
                                'format': 'bestaudio/best',
                                'outtmpl': r"C:\Notes\ytDlpTest\%(title)s.%(ext)s",
                                'postprocessors': [
                                    {
                                        'key': 'FFmpegExtractAudio',
                                        'preferredcodec': 'mp3',
                                        'preferredquality': '192',
                                    },
                                    {
                                        'key': 'FFmpegMetadata',
                                    }
                                ],
                                'quiet': False,
                                'no_warnings': True,       # Suppress warnings
                                'ignoreerrors': True,      # Skip videos that cause errors
                            }

                            with yt_dlp.YoutubeDL(ydl_opts) as ydl:
                                ydl.download([url])

                        # Przykładowe użycie:
                        download_as_mp3('https://www.youtube.com/watch?v=dQw4w9WgXcQ')

                    
                
blog
CW

YoutubeDLSharp czyli nugetowa paczka proxy pod yt-dlp

                    
                        using YoutubeDLSharp;

                        var ytdl = new YoutubeDL();
                        // set the path of yt-dlp and FFmpeg if they're not in PATH or current directory

                        ytdl.YoutubeDLPath = "C:\\MyScripts\\yt-dlp.exe";
                        ytdl.FFmpegPath = "C:\\MyScripts\\yt-dlg\\ffmpeg.exe";

                        // optional: set a different download folder
                        ytdl.OutputFolder = @"C:\Notes\ytDlpTestCSHARP";

                        // download a video
                        var res = await ytdl
                            .RunVideoDownload("https://www.youtube.com/watch?v=bq9ghmgqoyc");

                        // the path of the downloaded file
                        string path = res.Data;

                        Console.WriteLine($"Pobrano plik: {path}");
                    
                

Pobieranie filmików YouTube yt_dlp w C# jako proces

                    

















                        















                        using System.Diagnostics;

                        string youtubeUrl = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"; 
                        string ytDlpPath = @"C:\MyScripts\yt-dlp.exe";
                        string ffmpegPath = @"C:\MyScripts\yt-dlg\ffmpeg.exe";
                        string outputFolder = @"C:\Notes\ytDlpTestCSHARP";

                        DownloadYouTubeAsMp3(youtubeUrl, ytDlpPath, ffmpegPath, outputFolder);


                        static void DownloadYouTubeAsMp3(string url, string ytDlpPath, string ffmpegPath, string outputFolder)
                        {
                            if (!Directory.Exists(outputFolder))
                            {
                                Directory.CreateDirectory(outputFolder);
                            }

                            // Komenda yt-dlp do pobierania i konwersji na mp3
                            string arguments = $"--ffmpeg-location \"{ffmpegPath}\" -x --audio-format mp3 -o \"{outputFolder}\\%(title)s.%(ext)s\" \"{url}\"";

                            ProcessStartInfo psi = new ProcessStartInfo
                            {
                                FileName = ytDlpPath,
                                Arguments = arguments,
                                UseShellExecute = false,
                                RedirectStandardOutput = true,
                                RedirectStandardError = true,
                                CreateNoWindow = true
                            };

                            using (Process process = new Process())
                            {
                                process.StartInfo = psi;

                                process.OutputDataReceived += (sender, e) =>
                                {
                                    if (!string.IsNullOrEmpty(e.Data))
                                        Console.WriteLine("[yt-dlp] " + e.Data);
                                };

                                process.ErrorDataReceived += (sender, e) =>
                                {
                                    if (!string.IsNullOrEmpty(e.Data))
                                        Console.WriteLine("[error] " + e.Data);
                                };

                                process.Start();
                                process.BeginOutputReadLine();
                                process.BeginErrorReadLine();
                                process.WaitForExit();
                            }

                            Console.WriteLine("Pobieranie zakończone.");
                        }
                    
                
blog
CW

Pobieranie filmików YouTube EXPLODE w C#

                    


                        using YoutubeExplode;
                        using YoutubeExplode.Converter;

                        string videoUrl = "https://www.youtube.com/watch?v=dQw4w9WgXcQ";
                        string outputFolder = @"C:\Notes\ytDlpTestCSHARP";

                        if (!Directory.Exists(outputFolder))
                            Directory.CreateDirectory(outputFolder);

                        string outputPath = Path.Combine(outputFolder, "output.mp3");

                        var youtube = new YoutubeClient();

                        await youtube.Videos.DownloadAsync(
                            videoUrl,
                            outputPath,
                            builder => builder.SetContainer("mp3") // <- MP3!
                        );

                        Console.WriteLine($"Plik zapisany: {outputPath}");
                    
                
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW

Whisper w Python

                    

                        import whisper

                        def transcribe_video(video_path, output_path):
                            try:
                                model = whisper.load_model("base")
                                result = model.transcribe(video_path)

                                if not result or "text" not in result:
                                    print("Transkrypcja nie powiodła się.")
                                    return

                                with open(output_path, "w", encoding="utf-8") as f:
                                    f.write(result["text"])

                                print("Transkrypcja została zapisana pomyślnie.")
                            except Exception as e:
                                print(f"Wystąpił błąd: {e}")

                        if __name__ == "__main__":
                            video_path  = r"D:\WDI_GRAPHQL.mp4"
                            output_path = r"D:\WDI_GRAPHQL.txt"
                            transcribe_video(video_path, output_path)

                    
                

Whisper w Python praca procesora

                    
                        result = model.transcribe(video_path, fp16=False)
                    
                

Whisper w Python z animacją

                    


















                        import whisper
                        import threading
                        import time

                        def transcribe_video(video_path, output_path):
                        try:
                            # Załaduj model Whisper
                            model = whisper.load_model("base")

                            # Przygotuj mechanizm odliczania czasu
                            stop_event = threading.Event()
                            def timer():
                                start_time = time.time()
                                animation = "|/-\\"
                                idx = 0
                                while not stop_event.is_set():
                                    elapsed = int(time.time() - start_time)
                                    print(f"\rCzas trwania transkrypcji: {elapsed} sek {animation[idx % len(animation)]}", end="", flush=True)
                                    idx += 1
                                    time.sleep(1)
                                # na koniec nowe wiersz, żeby nie zostać na tej samej linii
                                print()

                            # Uruchom timer w tle
                            t = threading.Thread(target=timer)
                            t.start()

                            # Wykonaj transkrypcję (to może chwilę potrwać)
                            result = model.transcribe(video_path, fp16=False)

                            # Zatrzymaj timer
                            stop_event.set()
                            t.join()

                            # Sprawdź, czy transkrypcja się powiodła
                            if not result or "text" not in result:
                                print("Transkrypcja nie powiodła się.")
                                return

                            # Zapisz transkrypcję do pliku
                            with open(output_path, "w", encoding="utf-8") as f:
                                f.write(result["text"])

                            print("Transkrypcja została zapisana pomyślnie.")

                        except Exception as e:
                            print(f"Wystąpił błąd: {e}")

                        if __name__ == "__main__":
                        video_path  = r"D:\WDI_KAFKA.mp4"
                        output_path = r"D:\WDI_KAFKA.txt"
                        transcribe_video(video_path, output_path)
                    
                

Whisper tworzenie napisów SRT

                    
                        import whisper
                        from datetime import timedelta

                        def format_timestamp(seconds):
                            td = timedelta(seconds=seconds)
                            return str(td)[:-3].replace('.', ',')

                        def transcribe_to_srt(audio_path, srt_path):
                            model = whisper.load_model("base")  
                            # Możesz użyć "small", "medium" lub "large" dla lepszej dokładności
                            result = model.transcribe(audio_path)
                            segments = result['segments']

                            with open(srt_path, 'w', encoding='utf-8') as srt_file:
                                for i, segment in enumerate(segments, start=1):
                                    start = format_timestamp(segment['start'])
                                    end = format_timestamp(segment['end'])
                                    text = segment['text'].strip()
                                    srt_file.write(f"{i}\n{start} --> {end}\n{text}\n\n")

                        # Przykład użycia
                        transcribe_to_srt(r"D:\WDI_GRAPHQL.mp4",r"D:\WDI_GRAPHQL.SRT")
                    
                

whisper

                    





                        23
                        0:00:51,600 --> 0:00:54,440
                        Po raz na jakiś czas patrzę, co się zmienia w grawkulel

                        24
                        0:00:54,440 --> 0:00:56,320
                        i jeszcze to w kontekście Cisharpa i Dukneta.

                        25
                        0:00:56,320 --> 0:00:57,040
                        Dokładnie.

                        26
                        0:00:57,520 --> 0:00:59,320
                        I tak w ten sposób powstała ta prekcja.

                        27
                        0:00:59,320 --> 0:01:02,520
                        No po, mamy mnóstwo zakręć, mnóstwo sposobów,
                    
                
blog
CW
blog
CW

Whisper .NET w C#

                    







                        try
                        {
                            await transcriber.InitializeAsync("C:\\MyScripts\\whsipermodel\\ggml-base.bin");

                            // Transcribe MP3 file
                            string mp3Result = await transcriber.TranscribeAudioFileAsync
                                ("D:\\Cool Kids of death - 009 -piosenki o milosci.mp3");
                            Console.WriteLine("MP3 MUSIC Transcription:");
                            Console.WriteLine(mp3Result);

                            // Transcribe MP3 file
                            string mp3Result2 = await transcriber.TranscribeAudioFileAsync
                                (@"D:\Game Boy Advance: Incredible tech on just 2 AA Batteries.mp3");
                            Console.WriteLine("MP3 Transcription:");
                            Console.WriteLine(mp3Result2);

                            // Transcribe video file (audio track will be extracted)
                            string videoResult = await transcriber.TranscribeAudioFileAsync
                                ("D:\\AQP71Kcw8e.mp4");
                            Console.WriteLine("\nVideo Transcription:");
                            Console.WriteLine(videoResult);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"Error: {ex.Message}");
                        }
                        finally
                        {
                            transcriber.Dispose();
                        }

                    
                
blog
CW
blog
CW

WhisperTranscriber przy użyciu Whisper.NET w C#

                    





















































































                        public class WhisperTranscriber
                        {
                            private WhisperFactory whisperFactory;
                            private WhisperProcessor processor;

                            public async Task InitializeAsync(string modelPath = null)
                            {
                                // If no model path provided, download and use a default model
                                if (string.IsNullOrEmpty(modelPath))
                                {
                                    modelPath = await DownloadModelAsync();
                                }

                                // Verify model file exists
                                if (!File.Exists(modelPath))
                                {
                                    throw new FileNotFoundException($"Whisper model file not found: {modelPath}");
                                }

                                // Create Whisper factory and processor
                                whisperFactory = WhisperFactory.FromPath(modelPath);
                                processor = whisperFactory.CreateBuilder()
                                    .WithLanguage("auto")
                                    .Build();
                            }

                            public async Task<string> TranscribeAudioFileAsync(string inputFilePath)
                            {
                                try
                                {
                                    string wavFilePath = null;

                                    if (FFmpegConvertToWave.IsFFmpegAvailable() == true)
                                    {
                                        wavFilePath = NAudioConvertToWave.ConvertToWav(inputFilePath);
                                    }
                                    else
                                    {
                                        // Convert to 16kHz mono WAV using FFmpeg
                                        wavFilePath = await FFmpegConvertToWave.ConvertToWhisperFormatAsync(inputFilePath);
                                    }

                                    // Transcribe audio
                                    var fullTranscription = "";

                                    using (var fileStream = File.OpenRead(wavFilePath))
                                    {
                                        await foreach (var segment in processor.ProcessAsync(fileStream))
                                        {
                                            Console.WriteLine($"[{segment.Start:hh\\:mm\\:ss} -> {segment.End:hh\\:mm\\:ss}] {segment.Text}");
                                            fullTranscription += segment.Text + " ";
                                        }
                                    }

                                    // Clean up temporary file if it was created
                                    if (wavFilePath != inputFilePath && File.Exists(wavFilePath))
                                    {
                                        File.Delete(wavFilePath);
                                    }

                                    return fullTranscription.Trim();
                                }
                                catch (Exception ex)
                                {
                                    throw new Exception($"Error transcribing audio: {ex.Message}", ex);
                                }
                            }



                            private async Task<string> DownloadModelAsync()
                            {
                                var modelDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "WhisperModels");
                                Directory.CreateDirectory(modelDir);

                                var modelPath = Path.Combine(modelDir, "ggml-base.bin");

                                if (!File.Exists(modelPath))
                                {
                                    Console.WriteLine("Downloading Whisper base model...");
                                    using var client = new HttpClient();
                                    var url = "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.bin";

                                    try
                                    {
                                        var response = await client.GetAsync(url);
                                        response.EnsureSuccessStatusCode();

                                        await using var fileStream = File.Create(modelPath);
                                        await response.Content.CopyToAsync(fileStream);
                                        Console.WriteLine($"Model downloaded to: {modelPath}");
                                    }
                                    catch (Exception ex)
                                    {
                                        throw new Exception($"Failed to download model: {ex.Message}");
                                    }
                                }

                                return modelPath;
                            }

                            public void Dispose()
                            {
                                processor?.Dispose();
                                whisperFactory?.Dispose();
                            }
                        }
                    
                

FFmpegConvertToWave

                    







































                        


























                        using System.Diagnostics;

                        namespace ConsoleAppWhispernet;

                        public static class FFmpegConvertToWave
                        {
                            public static async Task<string> ConvertToWhisperFormatAsync(string inputFilePath)
                            {
                                if (!IsFFmpegAvailable())
                                {
                                    throw new Exception
                                    ("FFmpeg is not available. Please install FFmpeg or add it to your PATH.");
                                }

                                var outputPath = Path.ChangeExtension(inputFilePath, "_whisper.wav");

                                var arguments = 
                                    $"-i \"{inputFilePath}\" -ar 16000 -ac 1 -c:a pcm_s16le \"{outputPath}\" -y";

                                var processInfo = new ProcessStartInfo
                                {
                                    FileName = "ffmpeg",
                                    Arguments = arguments,
                                    RedirectStandardOutput = true,
                                    RedirectStandardError = true,
                                    UseShellExecute = false,
                                    CreateNoWindow = true
                                };

                                try
                                {
                                    using var process = Process.Start(processInfo);
                                    if (process == null)
                                    {
                                        throw new Exception("Failed to start FFmpeg process.");
                                    }

                                    var errorTask = process.StandardError.ReadToEndAsync();
                                    var outputTask = process.StandardOutput.ReadToEndAsync();

                                    await process.WaitForExitAsync();

                                    var errorOutput = await errorTask;
                                    var stdOutput = await outputTask;

                                    if (process.ExitCode != 0)
                                    {
                                        throw 
                                        new Exception($"FFmpeg conversion failed (Exit Code {process.ExitCode}): {errorOutput}");
                                    }

                                    if (!File.Exists(outputPath))
                                    {
                                        throw 
                                        new Exception("FFmpeg conversion completed but output file was not created.");
                                    }

                                    return outputPath;
                                }
                                catch (Exception ex)
                                {
                                    throw new Exception($"Error running FFmpeg: {ex.Message}", ex);
                                }
                            }


                            public static bool IsFFmpegAvailable()
                            {
                                try
                                {
                                    var processInfo = new ProcessStartInfo
                                    {
                                        FileName = "ffmpeg",
                                        Arguments = "-version",
                                        RedirectStandardOutput = true,
                                        UseShellExecute = false,
                                        CreateNoWindow = true
                                    };

                                    using var process = Process.Start(processInfo);
                                    process.WaitForExit();
                                    return process.ExitCode == 0;
                                }
                                catch
                                {
                                    return false;
                                }
                            }
                        }

                    
                
blog
CW

NAudioConvertToWave

                    

























































                        using NAudio.Wave;
                        using NAudio.Wave.SampleProviders;

                        namespace ConsoleAppWhispernet;

                        public static class NAudioConvertToWave
                        {
                            public static string ConvertToWav(string inputFilePath)
                            {
                                var extension = Path.GetExtension(inputFilePath).ToLower();
                                var outputPath = Path.ChangeExtension(inputFilePath, "_16khz.wav");

                                try
                                {
                                    using (var reader = CreateAudioReader(inputFilePath, extension))
                                    {
                                        var sampleProvider = ConvertTo16KhzMono
                                            (reader, out IDisposable resamplerToDispose);

                                        using (resamplerToDispose)
                                        {
                                            var waveProvider = new SampleToWaveProvider16(sampleProvider);
                                            WaveFileWriter.CreateWaveFile(outputPath, waveProvider);
                                        }
                                    }

                                    return outputPath;
                                }
                                catch (Exception ex)
                                {
                                    throw new Exception($"Error converting {inputFilePath} to 16kHz WAV: {ex.Message}", ex);
                                }
                            }


                            private static AudioFileReader CreateAudioReader
                                (string inputFilePath, string extension)
                            {
                                if (extension == ".mp3" || extension == ".wav")
                                {
                                    return new AudioFileReader(inputFilePath);
                                }
                                else
                                {
                                    // Convert unsupported formats using MediaFoundationReader to a WAV file in memory
                                    var tempReader = new MediaFoundationReader(inputFilePath);
                                    var tempWavPath = Path.GetTempFileName();
                                    WaveFileWriter.CreateWaveFile(tempWavPath, tempReader);
                                    tempReader.Dispose();

                                    return new AudioFileReader(tempWavPath);
                                }
                            }

                            private static ISampleProvider ConvertTo16KhzMono
                                (AudioFileReader reader, out IDisposable resamplerToDispose)
                            {
                                // First, convert to mono if needed
                                ISampleProvider monoProvider;
                                if (reader.WaveFormat.Channels == 1)
                                {
                                    monoProvider = reader; // Already mono
                                }
                                else
                                {
                                    monoProvider = new StereoToMonoSampleProvider(reader);
                                }

                                // Then, resample to 16000 Hz if needed
                                if (reader.WaveFormat.SampleRate != 16000)
                                {
                                    var resampler = new WdlResamplingSampleProvider(monoProvider, 16000);
                                    resamplerToDispose = reader; // Dispose reader at the end
                                    return resampler;
                                }

                                // No resampling needed
                                resamplerToDispose = reader; // Still need to dispose the reader
                                return monoProvider;
                            }
                        }
                    
                
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW

NVIDIA NeMo ASR Models in Python

                    
                        #pip install nemo_toolkit[asr] torch torchaudio

                        from nemo.collections.asr.models import EncDecCTCModel
                        import torch

                        # Load pretrained ASR model (Apache 2.0)
                        asr_model = EncDecCTCModel.from_pretrained("stt_en_conformer_ctc_large")

                        # Path to the audio file (WAV, 16kHz, mono)
                        audio_file = "audio.wav"

                        # Transcribe
                        transcription = asr_model.transcribe([audio_file])

                        print("Transcript:", transcription[0])

                        #In Batch example
                        audio_files = ["audio1.wav", "audio2.wav", "audio3.wav"]
                        transcriptions = asr_model.transcribe(audio_files)
                        for file, text in zip(audio_files, transcriptions):
                            print(f"{file}: {text}")
                    
                
blog
CW

Vosk

                    







                        





                        #pip install vosk
                        #pip install soundfile

                        from vosk import Model, KaldiRecognizer
                        import wave
                        import json

                        # Load model
                        model = Model("models/vosk-model-small-en-us-0.15")

                        # Open audio file
                        wf = wave.open("audio.wav", "rb")
                        if wf.getnchannels() != 1 or wf.getsampwidth() != 2 or wf.getframerate() != 16000:
                            raise ValueError("Audio must be WAV format Mono PCM 16kHz.")

                        # Recognizer
                        rec = KaldiRecognizer(model, wf.getframerate())

                        result_text = ""

                        while True:
                            data = wf.readframes(4000)
                            if len(data) == 0:
                                break
                            if rec.AcceptWaveform(data):
                                result = json.loads(rec.Result())
                                result_text += result.get("text", "") + " "

                        # Final result
                        final_result = json.loads(rec.FinalResult())
                        result_text += final_result.get("text", "")

                        print("Transcription:", result_text)

                    
                
blog
CW

Vosk

                    




















                        using NAudio.Wave;
                        using Vosk;

                        const string modelPath = "models/vosk-model-small-en-us-0.15";
                        const string audioPath = "audio.wav";

                        // Init Vosk
                        Vosk.Vosk.SetLogLevel(0);
                        var model = new Model(modelPath);

                        // Open WAV
                        using var waveReader = new WaveFileReader(audioPath);
                        if (waveReader.WaveFormat.Encoding != WaveFormatEncoding.Pcm ||
                            waveReader.WaveFormat.SampleRate != 16000 ||
                            waveReader.WaveFormat.Channels != 1)
                        {
                            throw new Exception("Audio file must be 16kHz, mono, PCM WAV.");
                        }

                        var recognizer = new VoskRecognizer(model, 16000.0f);

                        byte[] buffer = new byte[4096];
                        int bytesRead;
                        string fullText = "";

                        while ((bytesRead = waveReader.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            if (recognizer.AcceptWaveform(buffer, bytesRead))
                            {
                                var result = recognizer.Result();
                                fullText += ExtractText(result) + " ";
                            }
                        }

                        var final = recognizer.FinalResult();
                        fullText += ExtractText(final);

                        Console.WriteLine("Transcription:");
                        Console.WriteLine(fullText);

                        static string ExtractText(string json)
                        {
                            using var doc = System.Text.Json.JsonDocument.Parse(json);
                            return doc.RootElement.GetProperty("text").GetString();
                        }
                    
                
blog
CW
blog
CW

youtube_transcript_api

                    
                        from youtube_transcript_api import YouTubeTranscriptApi

                        # ID filmiku YouTube
                        video_id = '2uLGXe95kTo'

                        # Pobierz transkrypcję
                        transcript = YouTubeTranscriptApi.get_transcript(video_id)

                        # Wyświetl transkrypcję
                        for line in transcript:
                            print(line['text'])
                    
                
blog
CW
blog
CW

YoutubeExplode in C#

                    
                        using YoutubeExplode;

                        var videoId = "2uLGXe95kTo";
                        var youtube = new YoutubeClient();

                        var trackManifest = await 
                            youtube.Videos.ClosedCaptions.GetManifestAsync(videoId);

                        // Wybierz ścieżkę w języku angielskim (lub innym, np. "pl" dla polskiego)
                        var trackInfo = 
                            trackManifest.GetByLanguage("en") ?? trackManifest.Tracks[0];

                        var captions = await youtube.Videos.ClosedCaptions.GetAsync(trackInfo);

                        var r = captions.TryGetByTime(new TimeSpan(0, 0, 10));
                        Console.WriteLine(r.Text);

                        foreach (var caption in captions.Captions)
                        {
                            Console.WriteLine(caption.Text);
                        }
                    
                
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW

Ollama w pythonie

                    
                        import ollama

                        # Inicjalizacja klienta ollama z dokładnym adresem
                        client = ollama.Client(host='http://localhost:11434')

                        # Wywołanie modelu z przykładowym promptem
                        response = client.generate(
                            model="llama3",
                            prompt="Dlaczego trawa jest zielona?"
                        )

                        # Wyświetlenie odpowiedzi
                        print(response['response'])
                    
                
blog
CW

Ollama w C# z Microsoft.Extensions.AI

                    












                        using Microsoft.Extensions.AI;
                        using Microsoft.Extensions.DependencyInjection;
                        using Microsoft.Extensions.Hosting;

                        var builder = Host.CreateApplicationBuilder();

                        builder.Services.AddChatClient
                            (new OllamaChatClient(new Uri("http://localhost:11434"),
                            "llama3"));

                        var app = builder.Build();

                        var chatClient = app.Services.
                            GetRequiredService<IChatClient>();

                        var chatHistory = new List<ChatMessage>();

                        while (true)
                        {
                            Console.WriteLine("Enter your prompt:");
                            var userPrompt = Console.ReadLine();
                            chatHistory.Add(new ChatMessage(ChatRole.User, userPrompt));

                            Console.WriteLine("Response from AI:");
                            var chatResponse = "";
                            await foreach (var item in
                                chatClient.CompleteStreamingAsync(chatHistory))
                            {
                                // We're streaming the response,
                                // so we get each message as it arrives
                                Console.Write(item.Text);
                                chatResponse += item.Text;
                            }
                            chatHistory.Add(new ChatMessage(ChatRole.Assistant,
                                chatResponse));
                            Console.WriteLine();
                        }
                    
                

Ollama w C# z Microsoft.SemanticKerne

                    
                        


                        using Codeblaze.SemanticKernel.Connectors.Ollama;
                        using Microsoft.Extensions.DependencyInjection;
                        using Microsoft.SemanticKernel;
                        using Microsoft.SemanticKernel.ChatCompletion;

                        var builder = Kernel.CreateBuilder();

                        // Register HttpClient for Ollama API communication
                        builder.Services.AddTransient<HttpClient>();

                        // Configure Ollama chat completion service
                        builder.AddOllamaChatCompletion(
                            modelId: "llama3:8b",  // Replace with your desired model
                            baseUrl: "http://localhost:11434" // Ollama's default endpoint
                        );

                        var kernel = builder.Build();

                        var chatService = kernel.GetRequiredService<IChatCompletionService>();

                        var history = new ChatHistory();
                        history.AddUserMessage("What is the capital of Poland?");

                        var response = await chatService.GetChatMessageContentsAsync(history);
                        Console.WriteLine(response[^1].Content);

                        //< PackageReference Include = "Codeblaze.SemanticKernel.Connectors.Ollama" Version = "1.3.1" />
                        //< PackageReference Include = "Microsoft.SemanticKernel" Version = "1.54.0" />
                        //<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
                    
                
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW

IMAP4_SSL

                    











                        import imaplib
                        import email
                        from email.header import decode_header

                        # Dane logowania
                        username = "twoje@proton.me"
                        password = "twoje_hasło"

                        # Łączenie z serwerem IMAP Gmail
                        mail = imaplib.IMAP4_SSL("imap.gmail.com")
                        mail.login(username, password)

                        # Wybór skrzynki odbiorczej
                        mail.select("inbox")

                        # Wyszukiwanie wiadomości o określonym temacie i zawierających określone słowa
                        subject = "temat_wiadomości"
                        keywords = ["słowo1", "słowo2"]
                        search_criteria = f'(SUBJECT "{subject}")'
                        status, messages = mail.search(None, search_criteria)

                        if status == "OK":
                            email_ids = messages[0].split()
                            for email_id in email_ids:
                                status, msg_data = mail.fetch(email_id, "(RFC822)")
                                if status == "OK":
                                    raw_email = msg_data[0][1]
                                    email_message = email.message_from_bytes(raw_email)

                                    # Sprawdzanie, czy wiadomość zawiera określone słowa
                                    contains_keywords = all(keyword.lower() in email_message.get_payload().lower() for keyword in keywords)

                                    if contains_keywords:
                                        print(f"Znaleziono wiadomość o ID: {email_id}")
                                        print(f"Temat: {email_message['Subject']}")
                                        print(f"Od: {email_message['From']}")
                                        print(f"Data: {email_message['Date']}")
                                        print("Zawartość:")
                                        print(email_message.get_payload())
                                        print("=" * 50)

                        # Zamykanie połączenia
                        mail.close()
                        mail.logout()
                    
                
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW

googleapiclient.discovery

                    







































































                        import os.path
                        import base64
                        from google.auth.transport.requests import Request
                        from google.oauth2.credentials import Credentials
                        from google_auth_oauthlib.flow import InstalledAppFlow
                        from googleapiclient.discovery import build
                        from bs4 import BeautifulSoup

                        # If modifying these SCOPES, delete the file token.json.
                        SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']

                        def get_gmail_service():
                            """Shows basic usage of the Gmail API.
                            Lists the user's Gmail labels.
                            """
                            creds = None
                            # The file token.json stores the user's access and refresh tokens.
                            if os.path.exists(r'C:\Notes\token.json'):
                                creds = Credentials.from_authorized_user_file(r'C:\Notes\token.json', SCOPES)
                            # If there are no (valid) credentials available, let the user log in.
                            if not creds or not creds.valid:
                                if creds and creds.expired and creds.refresh_token:
                                    creds.refresh(Request())
                                else:
                                    flow = InstalledAppFlow.from_client_secrets_file(
                                        r'C:\Notes\credentials.json', SCOPES)
                                    creds = flow.run_local_server(port=0)
                                # Save the credentials for the next run
                                with open(r'C:\Notes\token.json', 'w') as token:
                                    token.write(creds.to_json())

                            service = build('gmail', 'v1', credentials=creds)
                            return service

                        def get_latest_emails(service, max_results=10):
                            results = service.users().messages().list(userId='me', maxResults=max_results).execute()
                            messages = results.get('messages', [])
                            return messages

                        def get_email_details(service, msg_id):
                            message = service.users().messages().get(userId='me', id=msg_id, format='full').execute()
                            payload = message.get('payload', {})
                            headers = payload.get('headers', [])
                            subject = next(header['value'] for header in headers if header['name'] == 'Subject')
                            sender = next(header['value'] for header in headers if header['name'] == 'From')
                            date = next(header['value'] for header in headers if header['name'] == 'Date')

                            # Extract the body of the email
                            if 'parts' in payload:
                                parts = payload['parts']
                                data = parts[0]['body']['data']
                            else:
                                data = payload['body']['data']
                            data = data.replace("-", "+").replace("_", "/")
                            decoded_data = base64.b64decode(data)
                            soup = BeautifulSoup(decoded_data, 'html.parser')
                            body = soup.get_text()

                            return {
                                'subject': subject,
                                'sender': sender,
                                'date': date,
                                'body': body
                            }

                        def filter_emails_by_keywords(emails, keywords):
                            filtered_emails = []
                            for email in emails:
                                for keyword in keywords:
                                    if keyword.lower() in email['subject'].lower() or keyword.lower() in email['body'].lower():
                                        filtered_emails.append(email)
                                        break
                            return filtered_emails

                        def main():
                            service = get_gmail_service()
                            messages = get_latest_emails(service)
                            emails = []
                            for message in messages:
                                email_details = get_email_details(service, message['id'])
                                emails.append(email_details)

                            keywords = ['C#', '.NET']  # Add your keywords here
                            filtered_emails = filter_emails_by_keywords(emails, keywords)

                            for email in filtered_emails:
                                print(f"Subject: {email['subject']}")
                                print(f"From: {email['sender']}")
                                print(f"Date: {email['date']}")
                                print(f"Body: {email['body']}")
                                print("\n")

                        if __name__ == '__main__':
                            main()

                    
                
blog
CW
blog
CW

EmailProcessor w C# = plik startowy

                    































































































                        using Google.Apis.Auth.OAuth2;
                        using Google.Apis.Gmail.v1;
                        using Google.Apis.Gmail.v1.Data;
                        using Google.Apis.Services;
                        using Google.Apis.Util.Store;
                        using Microsoft.Data.Sqlite;
                        using Microsoft.Extensions.Configuration;
                        using Microsoft.Extensions.DependencyInjection;
                        using Microsoft.Extensions.Hosting;
                        using Microsoft.Extensions.Logging;
                        using Microsoft.Extensions.Options;
                        using System.Text;
                        using System.Text.Json;

                        var host = CreateHostBuilder(args).Build();

                        var processor = host.Services.GetRequiredService<EmailProcessor>();
                        await processor.ProcessEmailsAsync();

                        static IHostBuilder CreateHostBuilder(string[] args) =>
                            Host.CreateDefaultBuilder(args)
                                .ConfigureAppConfiguration((context, config) =>
                                {
                                    config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
                                })
                                .ConfigureServices((context, services) =>
                                {
                                    services.Configure<AppConfig>(context.Configuration.GetSection("AppConfig"));
                                    services.AddScoped<IMyGmailService, MyGmailService>();
                                    services.AddScoped<IDatabaseService, DatabaseService>();
                                    services.AddScoped<IOllamaService, OllamaService>();
                                    services.AddScoped<IFileService, FileService>();
                                    services.AddScoped<EmailProcessor>();
                                });



                        public class AppConfig
                        {
                            public GmailConfig Gmail { get; set; }
                            public DatabaseConfig Database { get; set; }
                            public OllamaConfig Ollama { get; set; }
                            public FileConfig Files { get; set; }
                            public FilterConfig Filters { get; set; }
                        }

                        public class GmailConfig
                        {
                            public string CredentialsPath { get; set; }
                            public string ApplicationName { get; set; }
                            public List<string> Scopes { get; set; }
                        }

                        public class DatabaseConfig
                        {
                            public string ConnectionString { get; set; }
                        }

                        public class OllamaConfig
                        {
                            public string BaseUrl { get; set; }
                            public string Model { get; set; }
                            public string SummarizePrompt { get; set; }
                        }

                        public class FileConfig
                        {
                            public string OutputDirectory { get; set; }
                            public string FileNameFormat { get; set; }
                        }

                        public class FilterConfig
                        {
                            public List<string> SubjectKeywords { get; set; }
                            public List<string> ContentKeywords { get; set; }
                            public int MaxEmailsPerRun { get; set; }
                        }

                        public class ProcessedEmail
                        {
                            public string Id { get; set; }
                            public string Subject { get; set; }
                            public string From { get; set; }
                            public DateTime ReceivedDate { get; set; }
                            public DateTime ProcessedDate { get; set; }
                            public string SummaryFilePath { get; set; }
                            public bool IsProcessed { get; set; }
                        }

                        public class EmailContent
                        {
                            public string Id { get; set; }
                            public string Subject { get; set; }
                            public string From { get; set; }
                            public string Body { get; set; }
                            public DateTime ReceivedDate { get; set; }
                        }

                        public interface IMyGmailService
                        {
                            Task<List<EmailContent>> GetFilteredEmailsAsync(FilterConfig filters);
                        }

                        public interface IDatabaseService
                        {
                            Task InitializeDatabaseAsync();
                            Task<bool> IsEmailProcessedAsync(string emailId);
                            Task MarkEmailAsProcessedAsync(ProcessedEmail email);
                            Task<List<ProcessedEmail>> GetProcessedEmailsAsync();
                        }

                        public interface IOllamaService
                        {
                            Task<string> SummarizeEmailAsync(string emailContent);
                        }

                        public interface IFileService
                        {
                            Task<string> SaveSummaryAsync(EmailContent email, string summary);
                        }

                    
                

EmailProcessor w C# = plik konfiguracyjny

                    


















                        {
                        "Logging": {
                            "LogLevel": {
                            "Default": "Information",
                            "Microsoft": "Warning",
                            "Microsoft.Hosting.Lifetime": "Information"
                            }
                        },
                        "AppConfig": {
                            "Gmail": {
                            "CredentialsPath": "C:\\Notes\\credentials.json",
                            "ApplicationName": "Gmail Email Processor",
                            "Scopes": [
                                "https://www.googleapis.com/auth/gmail.readonly"
                            ]
                            },
                            "Database": {
                            "ConnectionString": "Data Source=emails.db"
                            },
                            "Ollama": {
                            "BaseUrl": "http://localhost:11434",
                            "Model": "llama2",
                            "SummarizePrompt": "Proszę o zwięzłe podsumowanie poniższego emaila w języku polskim. Skoncentruj się na najważniejszych informacjach, działaniach do podjęcia i kluczowych szczegółach. Maksymalnie 3-4 zdania.\n\nEmail:\n{EMAIL_CONTENT}\n\nPodsumowanie:"
                            },
                            "Files": {
                            "OutputDirectory": "summaries",
                            "FileNameFormat": "{DATE}_{TIME}_{SUBJECT}_{ID}.md"
                            },
                            "Filters": {
                            "SubjectKeywords": [
                                "faktura",
                                "płatność",
                                "zamówienie",
                                "projekt",
                                "spotkanie"
                            ],
                            "ContentKeywords": [
                                "pilne",
                                "deadline",
                                "termin",
                                "wymagane"
                            ],
                            "MaxEmailsPerRun": 50
                            }
                        }
                        }
                    
                

EmailProcessor w C# = moja usługa gmail

                    











































































































































                        public class MyGmailService : IMyGmailService
                        {
                            private readonly AppConfig _config;
                            private readonly ILogger<GmailService> _logger;
                            private GmailService _service;

                            public MyGmailService(IOptions<AppConfig> config, ILogger<GmailService> logger)
                            {
                                _config = config.Value;
                                _logger = logger;
                            }

                            private async Task<GmailService> GetServiceAsync()
                            {
                                if (_service != null) return _service;

                                UserCredential credential;
                                using (var stream = new FileStream(_config.Gmail.CredentialsPath, FileMode.Open, FileAccess.Read))
                                {
                                    credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                                        GoogleClientSecrets.Load(stream).Secrets,
                                        _config.Gmail.Scopes,
                                        "user",
                                        CancellationToken.None,
                                        new FileDataStore("token.json", true));
                                }

                                _service = new GmailService(new BaseClientService.Initializer()
                                {
                                    HttpClientInitializer = credential,
                                    ApplicationName = _config.Gmail.ApplicationName,
                                });

                                return _service;
                            }

                            public async Task<List<EmailContent>> GetFilteredEmailsAsync(FilterConfig filters)
                            {
                                try
                                {
                                    var service = await GetServiceAsync();
                                    var emails = new List<EmailContent>();

                                    // Budowanie zapytania wyszukiwania
                                    var query = BuildSearchQuery(filters);

                                    var request = service.Users.Messages.List("me");
                                    request.Q = query;
                                    request.MaxResults = filters.MaxEmailsPerRun;

                                    var response = await request.ExecuteAsync();

                                    if (response.Messages == null)
                                    {
                                        _logger.LogInformation("Nie znaleziono emaili spełniających kryteria");
                                        return emails;
                                    }

                                    foreach (var message in response.Messages)
                                    {
                                        var email = await GetEmailContentAsync(service, message.Id);
                                        if (email != null && IsEmailMatchingFilters(email, filters))
                                        {
                                            emails.Add(email);
                                        }
                                    }

                                    _logger.LogInformation($"Pobrano {emails.Count} emaili spełniających kryteria");
                                    return emails;
                                }
                                catch (Exception ex)
                                {
                                    _logger.LogError(ex, "Błąd podczas pobierania emaili z Gmail");
                                    throw;
                                }
                            }

                            private string BuildSearchQuery(FilterConfig filters)
                            {
                                var queryParts = new List<string>();

                                if (filters.SubjectKeywords?.Any() == true)
                                {
                                    var subjectQuery = string.Join(" OR ", filters.SubjectKeywords.Select(k => $"subject:{k}"));
                                    queryParts.Add($"({subjectQuery})");
                                }

                                // Dodaj więcej kryteriów wyszukiwania jeśli potrzeba
                                queryParts.Add("is:unread"); // Tylko nieprzeczytane

                                return string.Join(" ", queryParts);
                            }

                            private async Task<EmailContent> GetEmailContentAsync(GmailService service, string messageId)
                            {
                                try
                                {
                                    var request = service.Users.Messages.Get("me", messageId);
                                    var message = await request.ExecuteAsync();

                                    var email = new EmailContent
                                    {
                                        Id = messageId,
                                        Subject = GetHeaderValue(message.Payload.Headers, "Subject") ?? "Brak tematu",
                                        From = GetHeaderValue(message.Payload.Headers, "From") ?? "Nieznany nadawca",
                                        ReceivedDate = DateTimeOffset.FromUnixTimeMilliseconds(message.InternalDate ?? 0).DateTime,
                                        Body = ExtractEmailBody(message.Payload)
                                    };

                                    return email;
                                }
                                catch (Exception ex)
                                {
                                    _logger.LogError(ex, $"Błąd podczas pobierania treści emaila {messageId}");
                                    return null;
                                }
                            }

                            private string GetHeaderValue(IList<MessagePartHeader> headers, string name)
                            {
                                return headers?.FirstOrDefault(h => h.Name.Equals(name, StringComparison.OrdinalIgnoreCase))?.Value;
                            }

                            private string ExtractEmailBody(MessagePart payload)
                            {
                                var body = new StringBuilder();

                                if (payload.Body?.Data != null)
                                {
                                    var data = Convert.FromBase64String(payload.Body.Data.Replace('-', '+').Replace('_', '/'));
                                    body.Append(Encoding.UTF8.GetString(data));
                                }

                                if (payload.Parts != null)
                                {
                                    foreach (var part in payload.Parts)
                                    {
                                        body.Append(ExtractEmailBody(part));
                                    }
                                }

                                return body.ToString();
                            }

                            private bool IsEmailMatchingFilters(EmailContent email, FilterConfig filters)
                            {
                                // Sprawdź słowa kluczowe w temacie
                                if (filters.SubjectKeywords?.Any() == true)
                                {
                                    var hasSubjectMatch = filters.SubjectKeywords.Any(keyword =>
                                        email.Subject.Contains(keyword, StringComparison.OrdinalIgnoreCase));

                                    if (!hasSubjectMatch) return false;
                                }

                                // Sprawdź słowa kluczowe w treści
                                if (filters.ContentKeywords?.Any() == true)
                                {
                                    var hasContentMatch = filters.ContentKeywords.Any(keyword =>
                                        email.Body.Contains(keyword, StringComparison.OrdinalIgnoreCase));

                                    if (!hasContentMatch) return false;
                                }

                                return true;
                            }
                        }
                    
                

EmailProcessor w C# = cały proces

                    








































































                        public class EmailProcessor
                        {
                            private readonly IMyGmailService _gmailService;
                            private readonly IDatabaseService _databaseService;
                            private readonly IOllamaService _ollamaService;
                            private readonly IFileService _fileService;
                            private readonly AppConfig _config;
                            private readonly ILogger<EmailProcessor> _logger;

                            public EmailProcessor(
                                IMyGmailService gmailService,
                                IDatabaseService databaseService,
                                IOllamaService ollamaService,
                                IFileService fileService,
                                IOptions<AppConfig> config,
                                ILogger<EmailProcessor> logger)
                            {
                                _gmailService = gmailService;
                                _databaseService = databaseService;
                                _ollamaService = ollamaService;
                                _fileService = fileService;
                                _config = config.Value;
                                _logger = logger;
                            }

                            public async Task ProcessEmailsAsync()
                            {
                                try
                                {
                                    _logger.LogInformation("Rozpoczynanie przetwarzania emaili...");

                                    // Inicjalizuj bazę danych
                                    await _databaseService.InitializeDatabaseAsync();

                                    // Pobierz emaile z Gmail
                                    var emails = await _gmailService.GetFilteredEmailsAsync(_config.Filters);

                                    _logger.LogInformation($"Znaleziono {emails.Count} emaili do przetworzenia");

                                    var processedCount = 0;
                                    var skippedCount = 0;

                                    foreach (var email in emails)
                                    {
                                        try
                                        {
                                            // Sprawdź czy email był już przetworzony
                                            if (await _databaseService.IsEmailProcessedAsync(email.Id))
                                            {
                                                _logger.LogInformation($"Email {email.Id} był już przetworzony - pomijam");
                                                skippedCount++;
                                                continue;
                                            }

                                            _logger.LogInformation($"Przetwarzanie emaila: {email.Subject}");

                                            // Wyślij do Ollama w celu podsumowania
                                            var summary = await _ollamaService.SummarizeEmailAsync(
                                                $"Temat: {email.Subject}\nOd: {email.From}\nTreść: {email.Body}");

                                            // Zapisz podsumowanie do pliku
                                            var filePath = await _fileService.SaveSummaryAsync(email, summary);

                                            // Oznacz jako przetworzony w bazie danych
                                            var processedEmail = new ProcessedEmail
                                            {
                                                Id = email.Id,
                                                Subject = email.Subject,
                                                From = email.From,
                                                ReceivedDate = email.ReceivedDate,
                                                ProcessedDate = DateTime.Now,
                                                SummaryFilePath = filePath,
                                                IsProcessed = true
                                            };

                                            await _databaseService.MarkEmailAsProcessedAsync(processedEmail);

                                            processedCount++;
                                            _logger.LogInformation($"Email {email.Id} został pomyślnie przetworzony");
                                        }
                                        catch (Exception ex)
                                        {
                                            _logger.LogError(ex, $"Błąd podczas przetwarzania emaila {email.Id}");
                                        }
                                    }

                                    _logger
                                    .LogInformation
                                    ($"Zakończono przetwarzanie emaili. Przetworzono: {processedCount}, Pominięto: {skippedCount}");
                                }
                                catch (Exception ex)
                                {
                                    _logger.LogError(ex, "Błąd podczas przetwarzania emaili");
                                    throw;
                                }
                            }
                        }
                    
                

EmailProcessor w C# = plik konfiguracyjny

                    


















                        {
                        "Logging": {
                            "LogLevel": {
                            "Default": "Information",
                            "Microsoft": "Warning",
                            "Microsoft.Hosting.Lifetime": "Information"
                            }
                        },
                        "AppConfig": {
                            "Gmail": {
                            "CredentialsPath": "C:\\Notes\\credentials.json",
                            "ApplicationName": "Gmail Email Processor",
                            "Scopes": [
                                "https://www.googleapis.com/auth/gmail.readonly"
                            ]
                            },
                            "Database": {
                            "ConnectionString": "Data Source=emails.db"
                            },
                            "Ollama": {
                            "BaseUrl": "http://localhost:11434",
                            "Model": "llama2",
                            "SummarizePrompt": "Proszę o zwięzłe podsumowanie poniższego emaila w języku polskim. Skoncentruj się na najważniejszych informacjach, działaniach do podjęcia i kluczowych szczegółach. Maksymalnie 3-4 zdania.\n\nEmail:\n{EMAIL_CONTENT}\n\nPodsumowanie:"
                            },
                            "Files": {
                            "OutputDirectory": "summaries",
                            "FileNameFormat": "{DATE}_{TIME}_{SUBJECT}_{ID}.md"
                            },
                            "Filters": {
                            "SubjectKeywords": [
                                "faktura",
                                "płatność",
                                "zamówienie",
                                "projekt",
                                "spotkanie"
                            ],
                            "ContentKeywords": [
                                "pilne",
                                "deadline",
                                "termin",
                                "wymagane"
                            ],
                            "MaxEmailsPerRun": 50
                            }
                        }
                        }
                    
                

EmailProcessor w C# = moja usługa gmail

                    













































































































































































































































                        public class DatabaseService : IDatabaseService
                        {
                            private readonly string _connectionString;
                            private readonly ILogger<DatabaseService> _logger;

                            public DatabaseService(IOptions<AppConfig> config, ILogger<DatabaseService> logger)
                            {
                                _connectionString = config.Value.Database.ConnectionString;
                                _logger = logger;
                            }

                            public async Task InitializeDatabaseAsync()
                            {
                                try
                                {
                                    using var connection = new SqliteConnection(_connectionString);
                                    await connection.OpenAsync();

                                    var createTableCommand = @"
                                            CREATE TABLE IF NOT EXISTS ProcessedEmails (
                                                Id TEXT PRIMARY KEY,
                                                Subject TEXT NOT NULL,
                                                FromAddress TEXT NOT NULL,
                                                ReceivedDate DATETIME NOT NULL,
                                                ProcessedDate DATETIME NOT NULL,
                                                SummaryFilePath TEXT NOT NULL,
                                                IsProcessed BOOLEAN NOT NULL DEFAULT 1
                                            )";

                                    using var command = new SqliteCommand(createTableCommand, connection);
                                    await command.ExecuteNonQueryAsync();

                                    _logger.LogInformation("Baza danych została zainicjalizowana");
                                }
                                catch (Exception ex)
                                {
                                    _logger.LogError(ex, "Błąd podczas inicjalizacji bazy danych");
                                    throw;
                                }
                            }

                            public async Task<bool> IsEmailProcessedAsync(string emailId)
                            {
                                try
                                {
                                    using var connection = new SqliteConnection(_connectionString);
                                    await connection.OpenAsync();

                                    var query = "SELECT COUNT(*) FROM ProcessedEmails WHERE Id = @Id AND IsProcessed = 1";
                                    using var command = new SqliteCommand(query, connection);
                                    command.Parameters.AddWithValue("@Id", emailId);

                                    var count = Convert.ToInt32(await command.ExecuteScalarAsync());
                                    return count > 0;
                                }
                                catch (Exception ex)
                                {
                                    _logger.LogError(ex, $"Błąd podczas sprawdzania statusu emaila {emailId}");
                                    return false;
                                }
                            }

                            public async Task MarkEmailAsProcessedAsync(ProcessedEmail email)
                            {
                                try
                                {
                                    using var connection = new SqliteConnection(_connectionString);
                                    await connection.OpenAsync();

                                    var insertCommand = @"
                                            INSERT OR REPLACE INTO ProcessedEmails 
                                            (Id, Subject, FromAddress, ReceivedDate, ProcessedDate, SummaryFilePath, IsProcessed)
                                            VALUES (@Id, @Subject, @FromAddress, @ReceivedDate, @ProcessedDate, @SummaryFilePath, @IsProcessed)";

                                    using var command = new SqliteCommand(insertCommand, connection);
                                    command.Parameters.AddWithValue("@Id", email.Id);
                                    command.Parameters.AddWithValue("@Subject", email.Subject);
                                    command.Parameters.AddWithValue("@FromAddress", email.From);
                                    command.Parameters.AddWithValue("@ReceivedDate", email.ReceivedDate);
                                    command.Parameters.AddWithValue("@ProcessedDate", email.ProcessedDate);
                                    command.Parameters.AddWithValue("@SummaryFilePath", email.SummaryFilePath);
                                    command.Parameters.AddWithValue("@IsProcessed", email.IsProcessed);

                                    await command.ExecuteNonQueryAsync();
                                    _logger.LogInformation($"Email {email.Id} został oznaczony jako przetworzony");
                                }
                                catch (Exception ex)
                                {
                                    _logger.LogError(ex, $"Błąd podczas oznaczania emaila {email.Id} jako przetworzony");
                                    throw;
                                }
                            }

                            public async Task<List<ProcessedEmail>> GetProcessedEmailsAsync()
                            {
                                var emails = new List<ProcessedEmail>();

                                try
                                {
                                    using var connection = new SqliteConnection(_connectionString);
                                    await connection.OpenAsync();

                                    var query = "SELECT * FROM ProcessedEmails ORDER BY ProcessedDate DESC";
                                    using var command = new SqliteCommand(query, connection);
                                    using var reader = await command.ExecuteReaderAsync();

                                    while (await reader.ReadAsync())
                                    {
                                        emails.Add(new ProcessedEmail
                                        {
                                            Id = reader.GetString(reader.GetOrdinal("Id")),
                                            Subject = reader.GetString(reader.GetOrdinal("Subject")),
                                            From = reader.GetString(reader.GetOrdinal("FromAddress")),
                                            ReceivedDate = reader.GetDateTime(reader.GetOrdinal("ReceivedDate")),
                                            ProcessedDate = reader.GetDateTime(reader.GetOrdinal("ProcessedDate")),
                                            SummaryFilePath = reader.GetString(reader.GetOrdinal("SummaryFilePath")),
                                            IsProcessed = reader.GetBoolean(reader.GetOrdinal("IsProcessed"))
                                        });
                                    }
                                }
                                catch (Exception ex)
                                {
                                    _logger.LogError(ex, "Błąd podczas pobierania przetworzonych emaili");
                                }

                                return emails;
                            }
                        }



                        public class OllamaService : IOllamaService
                        {
                            private readonly HttpClient _httpClient;
                            private readonly OllamaConfig _config;
                            private readonly ILogger<OllamaService> _logger;

                            public OllamaService(IOptions<AppConfig> config, ILogger<OllamaService> logger)
                            {
                                _config = config.Value.Ollama;
                                _logger = logger;
                                _httpClient = new HttpClient();
                            }

                            public async Task<string> SummarizeEmailAsync(string emailContent)
                            {
                                try
                                {
                                    var prompt = _config.SummarizePrompt.Replace("{EMAIL_CONTENT}", emailContent);

                                    var requestBody = new
                                    {
                                        model = _config.Model,
                                        prompt = prompt,
                                        stream = false
                                    };

                                    var json = JsonSerializer.Serialize(requestBody);
                                    var content = new StringContent(json, Encoding.UTF8, "application/json");

                                    var response = await _httpClient.PostAsync($"{_config.BaseUrl}/api/generate", content);

                                    if (response.IsSuccessStatusCode)
                                    {
                                        var responseContent = await response.Content.ReadAsStringAsync();
                                        var ollamaResponse = JsonSerializer.Deserialize<OllamaResponse>(responseContent);

                                        _logger.LogInformation("Email został pomyślnie podsumowany przez Ollama");
                                        return ollamaResponse.response;
                                    }
                                    else
                                    {
                                        _logger.LogError($"Błąd Ollama API: {response.StatusCode}");
                                        return "Błąd podczas generowania podsumowania";
                                    }
                                }
                                catch (Exception ex)
                                {
                                    _logger.LogError(ex, "Błąd podczas komunikacji z Ollama");
                                    return "Błąd podczas generowania podsumowania";
                                }
                            }

                            private class OllamaResponse
                            {
                                public string response { get; set; }
                            }
                        }



                        public class FileService : IFileService
                        {
                            private readonly FileConfig _config;
                            private readonly ILogger<FileService> _logger;

                            public FileService(IOptions<AppConfig> config, ILogger<FileService> logger)
                            {
                                _config = config.Value.Files;
                                _logger = logger;
                            }

                            public async Task<string> SaveSummaryAsync(EmailContent email, string summary)
                            {
                                try
                                {
                                    // Utwórz katalog jeśli nie istnieje
                                    Directory.CreateDirectory(_config.OutputDirectory);

                                    // Generuj nazwę pliku
                                    var sanitizedSubject = SanitizeFileName(email.Subject);
                                    var fileName = _config.FileNameFormat
                                        .Replace("{DATE}", email.ReceivedDate.ToString("yyyy-MM-dd"))
                                        .Replace("{TIME}", email.ReceivedDate.ToString("HH-mm-ss"))
                                        .Replace("{SUBJECT}", sanitizedSubject)
                                        .Replace("{ID}", email.Id);

                                    var filePath = Path.Combine(_config.OutputDirectory, fileName);

                                    // Przygotuj zawartość pliku
                                    var fileContent = $@"# Podsumowanie Email
                        **Temat:** {email.Subject}
                        **Od:** {email.From}
                        **Data otrzymania:** {email.ReceivedDate:yyyy-MM-dd HH:mm:ss}
                        **ID Email:** {email.Id}

                        ## Podsumowanie
                        {summary}

                        ## Oryginalna treść
                        {email.Body}

                        ---
                        *Wygenerowano: {DateTime.Now:yyyy-MM-dd HH:mm:ss}*
                        ";

                                    await File.WriteAllTextAsync(filePath, fileContent, Encoding.UTF8);

                                    _logger.LogInformation($"Podsumowanie zapisano do pliku: {filePath}");
                                    return filePath;
                                }
                                catch (Exception ex)
                                {
                                    _logger.LogError(ex, "Błąd podczas zapisywania podsumowania do pliku");
                                    throw;
                                }
                            }

                            private string SanitizeFileName(string fileName)
                            {
                                var invalidChars = Path.GetInvalidFileNameChars();
                                foreach (var c in invalidChars)
                                {
                                    fileName = fileName.Replace(c, '_');
                                }

                                // Ogranicz długość nazwy pliku
                                if (fileName.Length > 50)
                                {
                                    fileName = fileName.Substring(0, 50);
                                }

                                return fileName;
                            }
                        }

                    
                
blog
CW
blog
CW
blog
CW
blog
CW

Zbudowanie SemanticScraper od zera

                    



























                        using HtmlAgilityPack;
                        using System.Text;
                        using System.Text.Json;
                        string maxDate = "2023-10-01";

                        string prompt = @$"
                        Oto zawartość strony internetowej. Wyciągnij listę linków (adresów URL) do artykułów dostępnych na tej stronie.
                        Zwróć tylko linki (1 link na linię) i tylko te, które są opublikowane od daty {maxDate} włącznie.
                        Zignoruj reklamy, menu, stopki itd.

                        Treść strony:
                        {{textContent}}
                        ";

                        await SemanticScraper.RunAsync("https://cezarywalenciuk.pl/blog/programing/archive", prompt);


                        class SemanticScraper
                        {
                            private static async Task<string> ScrapeHtmlAsync(string url)
                            {
                                var web = new HtmlWeb();
                                var doc = web.Load(url);
                                return doc.DocumentNode.InnerText;
                            }
                            
                            //odpytywanie ollamy jako REST API bez Microsoft.Extensions.AI
                            private static async Task<string> QueryOllamaAsync(string prompt)
                            {
                                var httpClient = new HttpClient();
                                var body = new
                                {
                                    model = "deepseek-r1:8b",
                                    prompt = prompt,
                                    stream = false
                                };

                                var json = JsonSerializer.Serialize(body);
                                var content = new StringContent(json, Encoding.UTF8, "application/json");
                                var response = await httpClient.PostAsync("http://localhost:11434/api/generate", content);
                                return await response.Content.ReadAsStringAsync();
                            }

                            public static async Task RunAsync(string url, string prompt)
                            {
                                Console.WriteLine($"Scraping: {url}");
                                var textContent = await ScrapeHtmlAsync(url);
                                prompt = prompt.Replace("{textContent}", textContent);

                                var response = await QueryOllamaAsync(prompt);
                                Console.WriteLine("Wynik z LLM:\n");
                                Console.WriteLine(response);
                            }
                        }
                    
                
blog
CW
blog
CW

SmartScraperGraph

                    





































































































































































































                        


                        from datetime import datetime, timedelta
                        from scrapegraphai.graphs import SmartScraperGraph
                        from scrapegraphai.utils import prettify_exec_info

                        # Konfiguracja dla Ollama (lokalny model)
                        graph_config = {
                            "llm": {
                        "model": "ollama/llama3.2:3b",  # lub inny model dostępny w Ollama
                        "temperature": 0.1,
                        "base_url": "http://localhost:11434",  # domyślny port Ollama
                            },
                            "verbose": True,
                            "headless": True,
                        }

                        def find_recent_articles(url, target_month_year, max_articles=10):
                            """
                            Znajdź najnowsze artykuły z określonego miesiąca i roku
                            
                            Args:
                        url: URL strony do przeszukania
                        target_month_year: format "YYYY-MM" (np. "2024-12")
                        max_articles: maksymalna liczba artykułów do znalezienia
                            """
                            
                            # Bardziej precyzyjny prompt z przykładem
                            prompt = f"""
                            Przeskanuj stronę pod adresem {url} i znajdź wszystkie linki do artykułów opublikowanych w miesiącu {target_month_year}.

                            WAŻNE: Odpowiedź musi być WYŁĄCZNIE w formacie JSON. Nie dodawaj żadnego tekstu przed ani po JSON-ie.

                            Format odpowiedzi (przykład):
                            [
                        {{
                            "title": "Tytuł artykułu",
                            "url": "https://example.com/article1",
                            "publication_date": "2024-12-15",
                            "description": "Opis artykułu"
                        }},
                        {{
                            "title": "Drugi artykuł",
                            "url": "https://example.com/article2", 
                            "publication_date": "2024-12-10",
                            "description": "Opis drugiego artykułu"
                        }}
                            ]

                            Maksymalnie {max_articles} artykułów. Odpowiedź musi być czystym JSON-em bez dodatkowego tekstu.
                            """

                            try:
                        # Tworzenie grafu scrapującego
                        smart_scraper_graph = SmartScraperGraph(
                            prompt=prompt,
                            source=url,
                            config=graph_config
                        )

                        # Wykonanie scrapowania
                        result = smart_scraper_graph.run()

                        # Próba wyciągnięcia JSON-a z odpowiedzi
                        cleaned_result = extract_json_from_response(result)

                        return cleaned_result

                            except Exception as e:
                        print(f"Błąd podczas scrapowania: {e}")
                        if hasattr(e, 'llm_output'):
                            print(f"LLM Output: {e.llm_output}")
                        return None
                            
                        def find_articles_with_keywords(url, keywords, date_filter=None):
                            """
                            Znajdź artykuły zawierające określone słowa kluczowe
                            
                            Args:
                        url: URL strony do przeszukania  
                        keywords: lista słów kluczowych
                        date_filter: opcjonalny filtr daty (format "YYYY-MM")
                            """
                            
                            keywords_str = ", ".join(keywords)
                            date_condition = f" opublikowane w {date_filter}" if date_filter else ""
                            
                            prompt = f"""
                            Znajdź wszystkie artykuły na tej stronie, które zawierają którekolwiek z następujących słów kluczowych: {keywords_str}{date_condition}.
                            
                            Dla każdego pasującego artykułu zwróć:
                            1. Tytuł
                            2. Pełny URL
                            3. Datę publikacji
                            4. Fragment tekstu zawierający słowo kluczowe
                            5. Które słowo kluczowe zostało znalezione
                            
                            Sortuj wyniki według relevancji i daty publikacji.
                            
                            Format odpowiedzi: JSON lista obiektów z polami:
                            - title
                            - url  
                            - publication_date
                            - relevant_snippet
                            - matched_keywords
                            """
                            
                            try:
                        smart_scraper_graph = SmartScraperGraph(
                            prompt=prompt,
                            source=url,
                            config=graph_config
                        )

                        result = smart_scraper_graph.run()
                        return result

                            except Exception as e:
                        print(f"Błąd podczas wyszukiwania z słowami kluczowymi: {e}")
                        return None

                        def scrape_news_website_articles(url, days_back=30):
                            """
                            Scrape artykułów z witryny informacyjnej z ostatnich N dni
                            """
                            
                            target_date = datetime.now() - timedelta(days=days_back)
                            date_str = target_date.strftime("%Y-%m-%d")
                            
                            prompt = f"""
                            Przeanalizuj tę stronę internetową i znajdź wszystkie artykuły informacyjne 
                            opublikowane od {date_str} do dzisiaj.
                            
                            Dla każdego artykułu wyodrębnij:
                            1. Pełny tytuł artykułu
                            2. Bezpośredni link do artykułu (pełny URL)
                            3. Datę i czas publikacji
                            4. Nazwę autora (jeśli dostępna)
                            5. Kategorię lub sekcję
                            6. Pierwsze 2-3 zdania artykułu jako podgląd
                            
                            Ignoruj:
                            - Reklamy
                            - Linki do innych stron
                            - Menu nawigacyjne
                            - Stopki strony
                            
                            Zwróć wyniki jako JSON array z obiektami zawierającymi pola:
                            title, url, publication_date, author, category, preview
                            
                            Sortuj chronologicznie od najnowszych.
                            """
                            
                            try:
                        smart_scraper_graph = SmartScraperGraph(
                            prompt=prompt,
                            source=url,
                            config=graph_config
                        )

                        result = smart_scraper_graph.run()
                        return result

                            except Exception as e:
                        print(f"Błąd podczas scrapowania witryny informacyjnej: {e}")
                        return None

                        # Przykłady użycia
                        if __name__ == "__main__":
                            
                            # Upewnij się, że Ollama jest uruchomiona lokalnie
                            print("Sprawdzanie połączenia z Ollama...")
                            
                            # Przykład 1: Znajdź artykuły z grudnia 2024
                            print("\n=== Wyszukiwanie artykułów===")
                            url1 = "https://cezarywalenciuk.pl/blog/programing/archive"  # Zastąp prawdziwym URL
                            
                            articles_blog = find_recent_articles(url1, "2022-12", max_articles=5)
                            
                            if articles_blog:
                        print("Znalezione artykuły:")
                        print(articles_blog)
                            
                            # Przykład 2: Wyszukaj artykuły ze słowami kluczowymi
                            print("\n=== Wyszukiwanie artykułów ze słowami kluczowymi ===")
                            keywords = ["C#", "ASP.NET", "Windows"]
                            
                            keyword_articles = find_articles_with_keywords(
                        url1, 
                        keywords, 
                        date_filter="2022-12"
                            )
                            
                            if keyword_articles:
                        print("Artykuły ze słowami kluczowymi:")
                        print(keyword_articles)
                            
                            # Przykład 3: Scraping witryny informacyjnej z ostatnich 7 dni
                            print("\n=== Najnowsze artykuły z ostatnich 900 dni ===")
                            news_url = "https://cezarywalenciuk.pl/blog/programing/archive"  # Zastąp prawdziwym URL
                            
                            recent_news = scrape_news_website_articles(news_url, days_back=900)
                            
                            if recent_news:
                        print("Najnowsze artykuły:")
                        print(recent_news)

                        # Funkcja pomocnicza do ładnego wyświetlania wyników
                        def display_articles(articles_data, max_display=5):
                            """Wyświetl artykuły w czytelnym formacie"""
                            
                            if not articles_data:
                        print("Brak danych do wyświetlenia")
                        return
                            
                            # Jeśli wynik jest stringiem JSON, spróbuj go sparsować
                            if isinstance(articles_data, str):
                        import json
                        try:
                            articles_data = json.loads(articles_data)
                        except:
                            print("Nie można sparsować danych JSON")
                            print(articles_data)
                            return
                            
                            # Jeśli to lista artykułów
                            if isinstance(articles_data, list):
                        print(f"\nZnaleziono {len(articles_data)} artykułów:")
                        print("=" * 60)

                        for i, article in enumerate(articles_data[:max_display]):
                            print(f"\n{i+1}. {article.get('title', 'Brak tytułu')}")
                            print(f"   URL: {article.get('url', 'Brak URL')}")
                            print(f"   Data: {article.get('publication_date', 'Brak daty')}")
                            if article.get('description'):
                                print(f"   Opis: {article['description'][:100]}...")
                            else:
                        print("Wyniki:")
                        print(articles_data)
                    
                
blog
CW
blog
CW
blog
CW

NotesService przy użyciu Elasticsearch

                    




















































































































              
                        






                        using Nest;
                        using NotesSearchApp.Models;

                        namespace NotesSearchApp.Services
                        {
                            public class NotesService : INotesService
                            {
                                private readonly IElasticClient _elasticClient;
                                private readonly IEmbeddingService _embeddingService;

                                public NotesService(IElasticClient elasticClient, IEmbeddingService embeddingService)
                                {
                                    _elasticClient = elasticClient;
                                    _embeddingService = embeddingService;
                                }

                                public async Task<SearchResult> SearchNotesAsync(SearchRequest request)
                                {
                                    var stopwatch = System.Diagnostics.Stopwatch.StartNew();
                                    
                                    var searchDescriptor = new SearchDescriptor<Note>()
                                        .Index("notes")
                                        .From((request.Page - 1) * request.PageSize)
                                        .Size(request.PageSize)
                                        .Highlight(h => h
                                            .Fields(
                                                f => f.Field(p => p.Title).PreTags("<mark>").PostTags("</mark>"),
                                                f => f.Field(p => p.Content).PreTags("<mark>").PostTags("</mark>").FragmentSize(150)
                                            )
                                        );

                                    switch (request.SearchType)
                                    {
                                        case SearchType.Text:
                                            searchDescriptor = searchDescriptor.Query(q => q
                                                .MultiMatch(m => m
                                                    .Query(request.Query)
                                                    .Fields(f => f
                                                        .Field(p => p.Title, 2.0)
                                                        .Field(p => p.Content)
                                                        .Field(p => p.Source)
                                                    )
                                                    .Type(TextQueryType.BestFields)
                                                    .Fuzziness(Fuzziness.Auto)
                                                )
                                            );
                                            break;

                                        case SearchType.Semantic:
                                            var queryEmbedding = await _embeddingService.GetEmbeddingAsync(request.Query);
                                            searchDescriptor = searchDescriptor.Query(q => q
                                                .ScriptScore(ss => ss
                                                    .Query(qq => qq.MatchAll())
                                                    .Script(s => s
                                                        .Source("cosineSimilarity(params.query_vector, 'title_embedding') + 1.0")
                                                        .Params(p => p.Add("query_vector", queryEmbedding))
                                                    )
                                                )
                                            );
                                            break;

                                        case SearchType.Hybrid:
                                            var hybridEmbedding = await _embeddingService.GetEmbeddingAsync(request.Query);
                                            searchDescriptor = searchDescriptor.Query(q => q
                                                .Bool(b => b
                                                    .Should(
                                                        s => s.MultiMatch(m => m
                                                            .Query(request.Query)
                                                            .Fields(f => f
                                                                .Field(p => p.Title, 2.0)
                                                                .Field(p => p.Content)
                                                                .Field(p => p.Source)
                                                            )
                                                            .Type(TextQueryType.BestFields)
                                                            .Fuzziness(Fuzziness.Auto)
                                                            .Boost(1.0)
                                                        ),
                                                        s => s.ScriptScore(ss => ss
                                                            .Query(qq => qq.MatchAll())
                                                            .Script(sc => sc
                                                                .Source("cosineSimilarity(params.query_vector, 'title_embedding') + 1.0")
                                                                .Params(p => p.Add("query_vector", hybridEmbedding))
                                                            )
                                                            .Boost(0.8)
                                                        )
                                                    )
                                                )
                                            );
                                            break;
                                    }

                                    var response = await _elasticClient.SearchAsync<Note>(searchDescriptor);
                                    stopwatch.Stop();

                                    if (!response.IsValid)
                                    {
                                        throw new Exception($"Elasticsearch error: {response.OriginalException?.Message}");
                                    }

                                    var notes = response.Documents.Zip(response.Hits, (doc, hit) => new NoteResult
                                    {
                                        Note = doc,
                                        Score = hit.Score ?? 0,
                                        Highlights = hit.Highlight?.Values
                                            .SelectMany(v => v)
                                            .ToArray() ?? Array.Empty<string>()
                                    }).ToList();

                                    return new SearchResult
                                    {
                                        Notes = notes,
                                        TotalCount = response.Total,
                                        Page = request.Page,
                                        PageSize = request.PageSize,
                                        Query = request.Query,
                                        SearchType = request.SearchType,
                                        ElapsedMilliseconds = stopwatch.Elapsed.TotalMilliseconds
                                    };
                                }

                                public async Task<Note?> GetNoteByIdAsync(string id)
                                {
                                    var response = await _elasticClient.GetAsync<Note>(id, g => g.Index("notes"));
                                    return response.IsValid ? response.Source : null;
                                }

                                public async Task<bool> IndexNoteAsync(Note note)
                                {
                                    // Generuj embedding dla tytułu
                                    note.TitleEmbedding = await _embeddingService.GetEmbeddingAsync(note.Title);
                                    
                                    var response = await _elasticClient.IndexAsync(note, i => i
                                        .Index("notes")
                                        .Id(note.Id)
                                        .Refresh(Refresh.WaitFor)
                                    );

                                    return response.IsValid;
                                }

                                public async Task<bool> DeleteNoteAsync(string id)
                                {
                                    var response = await _elasticClient.DeleteAsync<Note>(id, d => d
                                        .Index("notes")
                                        .Refresh(Refresh.WaitFor)
                                    );

                                    return response.IsValid;
                                }
                            }
                        }
                    
                
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW

SpaCy

                    
                        























































































                        








































































































































































































                        import spacy
                        from spacy import displacy
                        import sys
                        from typing import List, Dict, Tuple

                        class PolishNER:
                            """
                            Klasa do rozpoznawania jednostek nazwanych w języku polskim
                            """
                            
                            def __init__(self, model_name: str = "pl_core_news_sm"):
                                """
                                Inicjalizuje model spaCy dla języka polskiego
                                
                                Args:
                                    model_name: nazwa modelu spaCy (pl_core_news_sm lub pl_core_news_lg)
                                """
                                self.model_name = model_name
                                self.nlp = None
                                self._load_model()
                            
                            def _load_model(self):
                                """Ładuje model spaCy z obsługą błędów"""
                                try:
                                    self.nlp = spacy.load(self.model_name)
                                    print(f"✓ Załadowano model: {self.model_name}")
                                    print(f"✓ Wersja spaCy: {spacy.__version__}")
                                except OSError:
                                    print(f"❌ Model '{self.model_name}' nie jest zainstalowany!")
                                    print("Zainstaluj model poleceniem:")
                                    print(f"python -m spacy download {self.model_name}")
                                    print("\nDostępne modele polskie:")
                                    print("- pl_core_news_sm (mały, szybki)")
                                    print("- pl_core_news_lg (duży, dokładniejszy)")
                                    sys.exit(1)
                            
                            def extract_entities(self, text: str) -> List[Dict]:
                                """
                                Wyodrębnia jednostki nazwane z tekstu
                                
                                Args:
                                    text: tekst do analizy
                                    
                                Returns:
                                    Lista słowników z informacjami o jednostkach nazwanych
                                """
                                doc = self.nlp(text)
                                entities = []
                                
                                for ent in doc.ents:
                                    entity_info = {
                                        'text': ent.text,
                                        'label': ent.label_,
                                        'description': spacy.explain(ent.label_) or 'Nieznany typ',
                                        'start': ent.start_char,
                                        'end': ent.end_char,
                                        'confidence': getattr(ent, 'score', 'N/A')
                                    }
                                    entities.append(entity_info)
                                
                                return entities
                            
                            def analyze_text(self, text: str, show_details: bool = True) -> Dict:
                                """
                                Kompleksowa analiza tekstu z NER
                                
                                Args:
                                    text: tekst do analizy
                                    show_details: czy pokazać szczegółowe informacje
                                    
                                Returns:
                                    Słownik z wynikami analizy
                                """
                                doc = self.nlp(text)
                                
                                # Wyodrębnij jednostki nazwane
                                entities = self.extract_entities(text)
                                
                                # Statystyki
                                stats = {
                                    'total_tokens': len(doc),
                                    'total_entities': len(entities),
                                    'sentences': len(list(doc.sents)),
                                    'entity_types': len(set(ent['label'] for ent in entities))
                                }
                                
                                # Grupuj jednostki według typu
                                entities_by_type = {}
                                for ent in entities:
                                    label = ent['label']
                                    if label not in entities_by_type:
                                        entities_by_type[label] = []
                                    entities_by_type[label].append(ent)
                                
                                results = {
                                    'text': text,
                                    'entities': entities,
                                    'entities_by_type': entities_by_type,
                                    'statistics': stats
                                }
                                
                                if show_details:
                                    self._print_analysis(results)
                                
                                return results
                            
                            def _print_analysis(self, results: Dict):
                                """Wyświetla sformatowane wyniki analizy"""
                                text = results['text']
                                entities = results['entities']
                                entities_by_type = results['entities_by_type']
                                stats = results['statistics']
                                
                                print("=" * 80)
                                print("ANALIZA JEDNOSTEK NAZWANYCH (NER)")
                                print("=" * 80)
                                print(f"Tekst: '{text}'")
                                print(f"Model: {self.model_name}")
                                
                                # Statystyki - bezpieczne formatowanie
                                print(f"\n📊 STATYSTYKI:")
                                print(f"   Tokens: {stats.get('total_tokens', 0)}")
                                print(f"   Zdania: {stats.get('sentences', 0)}")
                                print(f"   Jednostki nazwane: {stats.get('total_entities', 0)}")
                                print(f"   Typy jednostek: {stats.get('entity_types', 0)}")
                                
                                if not entities:
                                    print("\n❌ Nie znaleziono żadnych jednostek nazwanych.")
                                    return
                                
                                # Wszystkie jednostki - bezpieczne formatowanie
                                print(f"\n🔍 WSZYSTKIE JEDNOSTKI NAZWANE:")
                                print(f"{'Nr':<3} {'Tekst':<20} {'Typ':<15} {'Opis':<25} {'Pozycja':<10}")
                                print("-" * 80)
                                
                                for i, ent in enumerate(entities, 1):
                                    # Bezpieczne pobieranie wartości
                                    text_val = str(ent.get('text', ''))[:19]  # Obetnij długie teksty
                                    label_val = str(ent.get('label', ''))[:14]
                                    desc_val = str(ent.get('description', ''))[:24]
                                    start_val = ent.get('start', 0)
                                    end_val = ent.get('end', 0)
                                    pos = f"{start_val}-{end_val}"
                                    
                                    print(f"{i:<3} {text_val:<20} {label_val:<15} {desc_val:<25} {pos:<10}")
                                
                                # Jednostki według typu
                                print(f"\n📂 JEDNOSTKI WEDŁUG TYPU:")
                                for label, ents in entities_by_type.items():
                                    description = spacy.explain(label) or 'Nieznany typ'
                                    print(f"\n{label} ({description}):")
                                    unique_entities = list(set(ent.get('text', '') for ent in ents))
                                    for entity_text in unique_entities:
                                        if entity_text:  # Sprawdź czy tekst nie jest pusty
                                            count = sum(1 for ent in ents if ent.get('text', '') == entity_text)
                                            print(f"   • {entity_text} ({count}x)")
                            
                            def get_entity_types_info(self) -> Dict[str, str]:
                                """
                                Zwraca informacje o typach jednostek nazwanych obsługiwanych przez model
                                """
                                # Typowe etykiety w modelach polskich spaCy
                                common_labels = {
                                    'PER': 'Osoby (imiona, nazwiska)',
                                    'PERSON': 'Osoby (imiona, nazwiska)', 
                                    'ORG': 'Organizacje (firmy, instytucje)',
                                    'LOC': 'Lokalizacje (miejsca, regiony)',
                                    'GPE': 'Jednostki geopolityczne (kraje, miasta)',
                                    'MISC': 'Różne (inne jednostki nazwane)',
                                    'DATE': 'Daty i okresy czasu',
                                    'TIME': 'Czas (godziny)',
                                    'MONEY': 'Kwoty pieniężne',
                                    'PERCENT': 'Procenty',
                                    'QUANTITY': 'Ilości i miary',
                                    'ORDINAL': 'Liczby porządkowe',
                                    'CARDINAL': 'Liczby główne'
                                }
                                
                                return common_labels
                            
                            def visualize_entities(self, text: str, style: str = "ent"):
                                """
                                Wizualizuje jednostki nazwane w tekście
                                
                                Args:
                                    text: tekst do wizualizacji
                                    style: styl wizualizacji ("ent" lub "dep")
                                """
                                doc = self.nlp(text)
                                
                                if style == "ent" and doc.ents:
                                    print("\n🎨 WIZUALIZACJA JEDNOSTEK NAZWANYCH:")
                                    print("=" * 50)
                                    
                                    # Tekst z podświetlonymi jednostkami
                                    highlighted_text = text
                                    offset = 0
                                    
                                    for ent in sorted(doc.ents, key=lambda x: x.start_char):
                                        start = ent.start_char + offset
                                        end = ent.end_char + offset
                                        
                                        replacement = f"[{ent.text}]({ent.label_})"
                                        highlighted_text = (highlighted_text[:start] + 
                                                        replacement + 
                                                        highlighted_text[end:])
                                        
                                        offset += len(replacement) - len(ent.text)
                                    
                                    print(highlighted_text)
                                    
                                    # Legenda
                                    print(f"\n📝 LEGENDA:")
                                    entity_types = set(ent.label_ for ent in doc.ents)
                                    for label in entity_types:
                                        description = spacy.explain(label) or 'Nieznany typ'
                                        print(f"   {label}: {description}")
                                
                                else:
                                    print("❌ Brak jednostek nazwanych do wizualizacji.")

                        def demo_polish_ner():
                            """
                            Demonstracja działania NER dla języka polskiego
                            """
                            print("🇵🇱 DEMO: Named Entity Recognition dla języka polskiego")
                            print("=" * 60)
                            
                            # Inicjalizuj NER
                            try:
                                ner = PolishNER("pl_core_news_sm")  # lub "pl_core_news_lg"
                            except SystemExit:
                                return
                            
                            # Przykładowe teksty polskie
                            sample_texts = [
                                "Jan Kowalski mieszka w Warszawie i pracuje w Microsoft.",
                                
                                "Prezydent Andrzej Duda spotkał się z premierem Mateuszem Morawieckim w Pałacu Prezydenckim w Warszawie 15 marca 2024 roku.",
                                
                                "PKN Orlen jest największą polską firmą petrochemiczną. Jej siedziba znajduje się w Płocku.",
                                
                                "Robert Lewandowski strzelił 3 gole w meczu Bayern Monachium przeciwko Borussii Dortmund na Signal Iduna Park.",
                                
                                "Uniwersytet Warszawski został założony w 1816 roku. Obecnie studiuje tam około 45 tysięcy studentów.",
                                
                                "Apple ogłosiło wczoraj nowy iPhone za 4999 złotych. Premiera odbędzie się 20 października w Cupertino."
                            ]
                            
                            # Pokaż informacje o typach jednostek
                            print("\n📋 OBSŁUGIWANE TYPY JEDNOSTEK NAZWANYCH:")
                            entity_types = ner.get_entity_types_info()
                            for label, desc in entity_types.items():
                                print(f"   {label:<10}: {desc}")
                            
                            # Analizuj każdy przykład
                            for i, text in enumerate(sample_texts, 1):
                                print(f"\n{'#' * 80}")
                                print(f"PRZYKŁAD {i}/{len(sample_texts)}")
                                print('#' * 80)
                                
                                # Analiza NER
                                results = ner.analyze_text(text, show_details=True)
                                
                                # Wizualizacja
                                ner.visualize_entities(text)
                                
                                if i < len(sample_texts):
                                    input(f"\n⏳ Naciśnij Enter aby przejść do przykładu {i+1}...")

                        def custom_ner_analysis():
                            """
                            Interaktywna analiza własnego tekstu
                            """
                            print("\n" + "="*60)
                            print("🔤 ANALIZA WŁASNEGO TEKSTU")
                            print("="*60)
                            
                            try:
                                ner = PolishNER("pl_core_news_sm")
                            except SystemExit:
                                return
                            
                            while True:
                                print("\nWpisz tekst do analizy (lub 'quit' aby zakończyć):")
                                user_text = input("Tekst: ").strip()
                                
                                if user_text.lower() in ['quit', 'exit', 'q']:
                                    break
                                
                                if not user_text:
                                    print("❌ Pusty tekst!")
                                    continue
                                
                                print("\n" + "-"*50)
                                results = ner.analyze_text(user_text, show_details=True)
                                ner.visualize_entities(user_text)

                        if __name__ == "__main__":
                            try:
                                # Uruchom demo
                                demo_polish_ner()
                                
                                # Opcjonalnie: analiza własnego tekstu
                                response = input("\n🤔 Chcesz przeanalizować własny tekst? (t/n): ")
                                if response.lower() in ['t', 'tak', 'y', 'yes']:
                                    custom_ner_analysis()
                                    
                            except KeyboardInterrupt:
                                print("\n\n👋 Zakończono program.")
                            except Exception as e:
                                print(f"\n❌ Wystąpił błąd: {e}")
                                print("Upewnij się, że spaCy jest poprawnie zainstalowane.")
                    
                
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW
blog
CW

Sprawdzenie jakość tekstu

                    
                        























































































                        





































































































































































































































                        using System.Text;
                        using System.Text.RegularExpressions;
                        using WeCantSpell.Hunspell;

                        // Zapewniamy poprawne kodowanie polskich znaków w konsoli
                        Console.OutputEncoding = Encoding.UTF8;

                        string folder = "dictionaries_UTF-8";
                        string language = "pl_PL";

                        // Przykładowy tekst OCR do oceny
                        string ocrText = "ING BANK To jest przykładowy tekst z OCR, który zawiera polskie znaki. A to telefon : 555 555 111";

                        // Test słownika - sprawdzenie podstawowych polskich słów
                        // Jeśli to nie działa to nic nie ma sensu
                        TestDictionary(language, folder);

                        double score = AssessOcrQuality(ocrText, language, folder);
                        int rating = ConvertScoreToRating(score);

                        Console.WriteLine($"Tekst OCR: {ocrText}");
                        Console.WriteLine($"Wynik oceny: {score:F2}");
                        Console.WriteLine($"Ocena jakości (0-5): {rating}");

                        // Szczegółowa analiza
                        var report = AnalyzeOcrQualityDetailed(ocrText, language, folder);
                        Console.WriteLine("\nSzczegółowy raport:");
                        Console.WriteLine(report);

                        try
                        {
                            Guid guid = Guid.NewGuid();
                            File.WriteAllLines($"wordThatMaybeShouldBeAdded/{guid}.txt", report.InvalidWordsList);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"An error occurred: {ex.Message}");
                        }


                        /// <summary>
                        /// Testuje słownik na podstawowych polskich słowach
                        /// </summary>
                        /// <param name="language">Kod języka słownika</param>
                        static void TestDictionary(string language, string folder)
                        {
                            try
                            {
                                Console.WriteLine("Testowanie słownika...");
                                var dictionaryPath = $"{folder}/{language}";

                                // Sprawdź czy pliki słownika istnieją
                                if (!File.Exists($"{dictionaryPath}.dic"))
                                    Console.WriteLine($"BŁĄD: Plik słownika {dictionaryPath}.dic nie istnieje!");

                                if (!File.Exists($"{dictionaryPath}.aff"))
                                    Console.WriteLine($"BŁĄD: Plik słownika {dictionaryPath}.aff nie istnieje!");

                                if (!File.Exists($"{dictionaryPath}.dic") || !File.Exists($"{dictionaryPath}.aff"))
                                {
                                    Console.WriteLine("Nie można znaleźć plików słownika. Sprawdź ścieżkę i nazwy plików.");
                                    return;
                                }

                                var dictionary = WordList.CreateFromFiles($"{dictionaryPath}.dic", $"{dictionaryPath}.aff");

                                // Spróbuj:
                                //var dictionaryData = File.ReadAllText($"{dictionaryPath}.dic", Encoding.UTF8);
                                //var affixData = File.ReadAllText($"{dictionaryPath}.aff", Encoding.UTF8);
                                //var dictionary = WordList.CreateFromStreams(
                                //    new MemoryStream(Encoding.UTF8.GetBytes(dictionaryData)),
                                //    new MemoryStream(Encoding.UTF8.GetBytes(affixData))
                                //);

                                // Lista podstawowych polskich słów do testowania
                                var testWords = new[] {
                                        "dom", "kot", "pies", "przykładowy", "zawiera", "tekst",
                                        "polski", "język", "słownik", "komputer"
                                    };

                                Console.WriteLine("Wyniki testu słownika:");
                                foreach (var word in testWords)
                                {
                                    bool isWordRecognized = dictionary.Check(word);
                                    Console.WriteLine($"  - {word}: {(isWordRecognized ? "rozpoznane" : "nierozpoznane")}");

                                    if (!isWordRecognized)
                                    {
                                        var suggestions = dictionary.Suggest(word).Take(3);
                                        Console.WriteLine($"    Sugestie: {string.Join(", ", suggestions)}");
                                    }
                                }

                                Console.WriteLine("Test słownika zakończony.\n");
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine($"Błąd podczas testowania słownika: {ex.Message}");
                                Console.WriteLine($"Stack trace: {ex.StackTrace}");
                            }
                        }

                        /// <summary>
                        /// Ocena jakości tekstu OCR przy użyciu metody słownikowej
                        /// </summary>
                        /// <param name="text">Tekst OCR do oceny</param>
                        /// <param name="language">Kod języka (np. "pl_PL" dla polskiego)</param>
                        /// <returns>Współczynnik jakości (0.0-1.0)</returns>
                        static double AssessOcrQuality(string text, string language, string folder)
                        {
                            try
                            {
                                // Załadowanie słownika Hunspell
                                var dictionaryPath = $"{folder}/{language}";
                                var dictionary = WordList.CreateFromFiles($"{dictionaryPath}.dic", $"{dictionaryPath}.aff");

                                // Dzielenie tekstu na słowa z zachowaniem polskich znaków
                                var words = ExtractWords(text);

                                if (words.Length == 0)
                                    return 0.0;

                                // Lista rozpoznanych i nierozpoznanych słów dla diagnostyki
                                List<string> recognizedWords = new List<string>();
                                List<string> unrecognizedWords = new List<string>();

                                // Zliczanie słów, które istnieją w słowniku
                                int validWords = 0;
                                foreach (var word in words)
                                {
                                    if (dictionary.Check(word))
                                    {
                                        validWords++;
                                        recognizedWords.Add(word);
                                    }
                                    else
                                    {
                                        unrecognizedWords.Add(word);
                                    }
                                }

                                // Diagnostyka - wypisz rozpoznane i nierozpoznane słowa
                                Console.WriteLine("\nDiagnostyka słownika:");
                                Console.WriteLine($"Rozpoznane słowa ({recognizedWords.Count}): {string.Join(", ", recognizedWords)}");
                                Console.WriteLine($"Nierozpoznane słowa ({unrecognizedWords.Count}): {string.Join(", ", unrecognizedWords)}");

                                // Obliczanie współczynnika jakości
                                double qualityScore = (double)validWords / words.Length;

                                // Uwzględnienie długości tekstu (krótkie teksty mogą być mniej wiarygodne)
                                double lengthFactor = Math.Min(1.0, words.Length / 20.0);
                                qualityScore *= (0.7 + 0.3 * lengthFactor);

                                return qualityScore;
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine($"Błąd podczas oceny jakości OCR: {ex.Message}");
                                return 0.0;
                            }
                        }

                        /// <summary>
                        /// Konwertuje współczynnik jakości (0.0-1.0) na ocenę w skali 0-5
                        /// </summary>
                        /// <param name="score">Współczynnik jakości (0.0-1.0)</param>
                        /// <returns>Ocena w skali 0-5</returns>
                        static int ConvertScoreToRating(double score)
                        {
                            // Mapowanie współczynnika jakości na skalę 0-5
                            if (score < 0.2) return 0;
                            if (score < 0.4) return 1;
                            if (score < 0.6) return 2;
                            if (score < 0.75) return 3;
                            if (score < 0.9) return 4;
                            return 5;
                        }

                        /// <summary>
                        /// Wyodrębnia słowa z tekstu, zachowując znaki diakrytyczne
                        /// </summary>
                        /// <param name="text">Tekst do analizy</param>
                        /// <returns>Tablica wyodrębnionych słów</returns>
                        static string[] ExtractWords(string text)
                        {
                            // Konwersja do małych liter z zachowaniem polskich znaków
                            text = text.ToLowerInvariant();

                            // Używamy wyrażenia regularnego obsługującego znaki Unicode
                            return Regex.Split(text, @"[^\p{L}]+")
                                        .Where(w => !string.IsNullOrWhiteSpace(w))
                                        .ToArray();
                        }

                        /// <summary>
                        /// Szczegółowa analiza jakości OCR
                        /// </summary>
                        /// <param name="text">Tekst OCR do oceny</param>
                        /// <param name="language">Kod języka (np. "pl_PL" dla polskiego)</param>
                        /// <returns>Szczegółowe informacje o jakości OCR</returns>
                        static OcrQualityReport AnalyzeOcrQualityDetailed(string text, string language, string folder)
                        {
                            try
                            {
                                var dictionaryPath = $"{folder}/{language}";

                                // Używamy niestandardowego słownika, jeśli istnieje
                                var dictionaryToUse = dictionaryPath;
                                if (!File.Exists($"{dictionaryPath}.dic") || !File.Exists($"{dictionaryPath}.aff"))
                                {
                                    Console.WriteLine($"UWAGA: Słownik {dictionaryPath} nie istnieje, próba użycia słownika systemowego...");
                                    // Może być potrzebna inna lokalizacja słownika systemowego
                                    dictionaryToUse = "pl";
                                }

                                var dictionary = WordList.CreateFromFiles($"{dictionaryToUse}.dic", $"{dictionaryToUse}.aff");

                                // Wyodrębnienie słów z tekstu
                                var words = ExtractWords(text);

                                List<string> validWords = new List<string>();
                                List<string> invalidWords = new List<string>();
                                Dictionary<string, List<string>> suggestions = new Dictionary<string, List<string>>();

                                // Zbiór znanych słów kluczowych - słownikowe rozszerzenie
                                HashSet<string> knownKeywords = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase)
                                    {
                                        "ocr", "pdf", "xml", "jpeg", "png", "tiff"
                                    };

                                foreach (var word in words)
                                {
                                    // Specjalne traktowanie dla akronimów i znanych słów kluczowych
                                    if (knownKeywords.Contains(word))
                                    {
                                        validWords.Add(word);
                                        continue;
                                    }

                                    if (dictionary.Check(word))
                                        validWords.Add(word);
                                    else
                                    {
                                        invalidWords.Add(word);

                                        // Pobierz sugestie poprawek dla niepoprawnych słów
                                        var wordSuggestions = dictionary.Suggest(word)
                                                                    .ToList();

                                        if (wordSuggestions.Any())
                                            suggestions[word] = wordSuggestions;

                                        // Jeśli słowo ma sugestię samego siebie, to oznacza problem ze słownikiem
                                        if (wordSuggestions.Contains(word))
                                        {
                                            Console.WriteLine($"UWAGA: Słowo '{word}' nie jest rozpoznawane przez słownik, " +
                                                            $"ale jest sugerowane jako poprawka dla samego siebie!");
                                        }
                                    }
                                }

                                double qualityScore = words.Length > 0 ? (double)validWords.Count / words.Length : 0;
                                int rating = ConvertScoreToRating(qualityScore);

                                return new OcrQualityReport
                                {
                                    TotalWords = words.Length,
                                    ValidWords = validWords.Count,
                                    InvalidWords = invalidWords.Count,
                                    InvalidWordsList = invalidWords,
                                    ValidWordsList = validWords,
                                    Suggestions = suggestions,
                                    QualityScore = qualityScore,
                                    Rating = rating,
                                    UsedDictionary = dictionaryToUse
                                };
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine($"Błąd podczas szczegółowej analizy OCR: {ex.Message}");
                                return new OcrQualityReport
                                {
                                    TotalWords = 0,
                                    ValidWords = 0,
                                    InvalidWords = 0,
                                    QualityScore = 0,
                                    Rating = 0,
                                    ErrorMessage = ex.Message
                                };
                            }
                        }

                        /// <summary>
                        /// Klasa przechowująca szczegółowe informacje o jakości OCR
                        /// </summary>
                        public class OcrQualityReport
                        {
                            public int TotalWords { get; set; }
                            public int ValidWords { get; set; }
                            public int InvalidWords { get; set; }
                            public List<string> ValidWordsList { get; set; } = new List<string>();
                            public List<string> InvalidWordsList { get; set; } = new List<string>();
                            public Dictionary<string, List<string>> Suggestions { get; set; } = new Dictionary<string, List<string>>();
                            public double QualityScore { get; set; }
                            public int Rating { get; set; }
                            public string? UsedDictionary { get; set; }
                            public string? ErrorMessage { get; set; }

                            public override string ToString()
                            {
                                if (!string.IsNullOrEmpty(ErrorMessage))
                                {
                                    return $"Błąd: {ErrorMessage}";
                                }

                                var result = $"Łącznie słów: {TotalWords}\n" +
                                    $"Poprawnych słów: {ValidWords}\n" +
                                    $"Niepoprawnych słów: {InvalidWords}\n" +
                                    $"Współczynnik jakości: {QualityScore:F2}\n" +
                                    $"Ocena (0-5): {Rating}\n" +
                                    $"Użyty słownik: {UsedDictionary}";

                                if (ValidWords > 0)
                                {
                                    result += "\n\nPoprawne słowa: " + string.Join(", ", ValidWordsList);
                                }

                                if (InvalidWords > 0)
                                {
                                    result += "\n\nNiepoprawne słowa:";
                                    foreach (var word in InvalidWordsList)
                                    {
                                        result += $"\n- {word}";
                                        if (Suggestions.ContainsKey(word) && Suggestions[word].Any())
                                        {
                                            result += $" (sugestie: {string.Join(", ", Suggestions[word])})";
                                        }
                                    }
                                }

                                return result;
                            }
                        }
                    
                
blog
CW
blog
CW