feat: clamav tarama sistemi ve hata yönetimi iyileştirmeleri ekle

ClamAV entegrasyonu ile indirilen altyazı dosyalarının otomatik virüs taraması
eklendi. Pipeline tabanlı hata yönetimi sistemi ile hatalar kategorize edilip
daha iyi işleniyor. Türkcealtyazi sağlayıcısı TV dizileri için sezon/bölüm
bazlı eşleştirme ve paket indirme desteği kazandı. Dosya izleyicide olay
çiftleme (deduplication) mekanizması eklendi. Metin kodlaması normalizasyonu
Türkçe karakterler için geliştirildi.
This commit is contained in:
2026-02-16 13:44:42 +03:00
parent d38fc3b390
commit 4606970577
29 changed files with 2609 additions and 100 deletions

View File

@@ -0,0 +1,33 @@
import { describe, expect, it } from 'vitest';
import iconv from 'iconv-lite';
import { normalizeSubtitleBuffer } from '../src/utils/file.js';
describe('normalizeSubtitleBuffer', () => {
const sample = '1\r\n00:00:01,000 --> 00:00:02,000\r\nÇığ ÖşÜ ıİ\r\n';
it('keeps valid utf8 Turkish characters intact', () => {
const buf = Buffer.from(sample, 'utf8');
const out = normalizeSubtitleBuffer(buf);
expect(out).toBe('1\n00:00:01,000 --> 00:00:02,000\nÇığ ÖşÜ ıİ\n');
});
it('decodes windows-1254 Turkish text correctly', () => {
const buf = iconv.encode(sample, 'windows-1254');
const out = normalizeSubtitleBuffer(buf);
expect(out).toBe('1\n00:00:01,000 --> 00:00:02,000\nÇığ ÖşÜ ıİ\n');
});
it('decodes utf16-le BOM correctly', () => {
const buf = iconv.encode(sample, 'utf16-le');
const withBom = Buffer.concat([Buffer.from([0xff, 0xfe]), buf]);
const out = normalizeSubtitleBuffer(withBom);
expect(out).toBe('1\n00:00:01,000 --> 00:00:02,000\nÇığ ÖşÜ ıİ\n');
});
it('repairs mojibake form with \u00fd/\u00fe/\u00f0 Turkish corruption', () => {
const mojibake = '1\r\n00:00:01,000 --> 00:00:02,000\r\nKýz þöyle dedi: aðýr bir iþ.\r\n';
const buf = Buffer.from(mojibake, 'utf8');
const out = normalizeSubtitleBuffer(buf);
expect(out).toContain('Kız şöyle dedi: ağır bir iş.');
});
});

View File

@@ -0,0 +1,19 @@
import { describe, expect, it } from 'vitest';
import { createEventDeduper } from '../src/watcher/dedup.js';
describe('createEventDeduper', () => {
it('suppresses repeated events inside window for same path', () => {
const dedupe = createEventDeduper(1000);
expect(dedupe('/media/movie/a.mkv', 1000)).toBe(true);
expect(dedupe('/media/movie/a.mkv', 1500)).toBe(false);
expect(dedupe('/media/movie/a.mkv', 2001)).toBe(true);
});
it('does not suppress different paths', () => {
const dedupe = createEventDeduper(1000);
expect(dedupe('/media/movie/a.mkv', 1000)).toBe(true);
expect(dedupe('/media/movie/b.mkv', 1200)).toBe(true);
});
});