diff --git a/internal/indexer/search.go b/internal/indexer/search.go index c3093ce..1787be2 100644 --- a/internal/indexer/search.go +++ b/internal/indexer/search.go @@ -8,7 +8,7 @@ import ( pb "homelab.lan/music-agregator/gen/music_agregator/indexer/v1" "homelab.lan/music-agregator/internal/release" - "homelab.lan/music-agregator/internal/tracker/rutracker" + "homelab.lan/music-agregator/internal/tracker" ) type SearchResult struct { @@ -90,15 +90,13 @@ func (sr *SearchResponse) ToProto() *pb.SearchResponse { return &pb.SearchResponse{Result: pbItems} } -var ( - rutrackerParserFactory = rutracker.NewRuTrackerParserFactory() -) +var genericParser = tracker.NewGenericParser() func (sr *SearchResult) ToSearchResponse() *SearchResponse { var items []*SearchItemResult for _, item := range sr.Items { - rel := rutrackerParserFactory.GetParser(item.Categories).Parse(item.Title) + rel := genericParser.Parse(item.Title) log.Trace(). Str("tracker", item.JackettIndexer.ID). diff --git a/internal/tracker/factory.go b/internal/tracker/factory.go deleted file mode 100644 index 84d369a..0000000 --- a/internal/tracker/factory.go +++ /dev/null @@ -1,5 +0,0 @@ -package tracker - -type ParserFactory interface { - GetParser(categories []string) Parser -} diff --git a/internal/tracker/parser.go b/internal/tracker/parser.go deleted file mode 100644 index 87d1576..0000000 --- a/internal/tracker/parser.go +++ /dev/null @@ -1,7 +0,0 @@ -package tracker - -import "homelab.lan/music-agregator/internal/release" - -type Parser interface { - Parse(title string) *release.Release -} diff --git a/internal/tracker/rutracker/category.go b/internal/tracker/rutracker/category.go deleted file mode 100644 index 4133637..0000000 --- a/internal/tracker/rutracker/category.go +++ /dev/null @@ -1,404 +0,0 @@ -package rutracker - -var RockForumIDs = []int{ - 1698, // Зарубежный Rock (parent) - 1702, // Classic Rock & Hard Rock (lossless) - 1703, // Classic Rock & Hard Rock (lossy) - 1704, // Progressive & Art-Rock (lossless) - 1705, // Progressive & Art-Rock (lossy) - 1706, // Folk-Rock (lossless) - 1707, // Folk-Rock (lossy) - 1708, // Pop-Rock & Soft Rock (lossless) - 1709, // Pop-Rock & Soft Rock (lossy) - 1710, // Instrumental Guitar Rock (lossless) - 1711, // Instrumental Guitar Rock (lossy) - 1712, // Rockabilly, Psychobilly, Rock'n'Roll (lossless) - 1713, // Rockabilly, Psychobilly, Rock'n'Roll (lossy) - 1714, // Восточноазиатский рок (lossless) - 1715, // Восточноазиатский рок (lossy) - 722, // Отечественный Rock, Metal (parent) - 951, // Rock на языках народов xUSSR (lossless) - 952, // Rock на языках народов xUSSR (lossy) - 172, // Post-Punk, Shoegaze, Garage Rock, Noise Rock (lossless) - 236, // Post-Punk, Shoegaze, Garage Rock, Noise Rock (lossy) - 2175, // Avant-garde, Experimental Rock (lossless) - 2174, // Avant-garde, Experimental Rock (lossy) - 2329, // AOR (Melodic Hard Rock, Arena rock) (lossless) - 2330, // AOR (Melodic Hard Rock, Arena rock) (lossy) - 731, // Сборники зарубежного рока (lossless) - 1799, // Сборники зарубежного рока (lossy) - 737, // Rock (lossless) - 738, // Rock (lossy) -} - -var MetalForumIDs = []int{ - 1716, // Зарубежный Metal (parent) - 739, // Metal (lossless) - 740, // Metal (lossy) - 1719, // Black (lossless) - 1778, // Black (lossy) - 1720, // Folk, Pagan, Viking (lossless) - 798, // Folk, Pagan, Viking (lossy) - 1724, // Gothic Metal (lossless) - 1725, // Gothic Metal (lossy) - 1726, // Heavy, Power, Progressive (lossless) - 1727, // Heavy, Power, Progressive (lossy) - 1728, // Thrash, Speed (lossless) - 1729, // Thrash, Speed (lossy) - 1730, // Grind, Brutal Death (lossless) - 1731, // Grind, Brutal Death (lossy) - 1779, // Death, Doom (lossless) - 1780, // Death, Doom (lossy) - 1796, // Avant-garde, Experimental Metal (lossless) - 1797, // Avant-garde, Experimental Metal (lossy) - 1815, // Sludge, Stoner, Post-Metal (lossless) - 1816, // Sludge, Stoner, Post-Metal (lossy) - 1766, // Зарубежный и Отечественный Metal (оцифровки) -} - -var AlternativeForumIDs = []int{ - 1732, // Зарубежные Alternative, Punk, Independent (parent) - 464, // Alternative, Punk, Independent (lossless) - 463, // Alternative, Punk, Independent (lossy) - 123, // Alternative, Punk, Independent (оцифровки) - 1736, // Alternative & Nu-metal (lossless) - 1737, // Alternative & Nu-metal (lossy) - 1738, // Punk (lossless) - 1739, // Punk (lossy) - 1740, // Hardcore (lossless) - 1741, // Hardcore (lossy) - 1742, // Post-Rock (lossless) - 1743, // Post-Rock (lossy) - 1744, // Industrial & Post-industrial (lossless) - 1745, // Industrial & Post-industrial (lossy) - 1746, // Emocore, Post-hardcore, Metalcore (lossless) - 1747, // Emocore, Post-hardcore, Metalcore (lossy) - 1748, // Gothic Rock & Dark Folk (lossless) - 1749, // Gothic Rock & Dark Folk (lossy) - 1773, // Indie Rock, Indie Pop, Dream Pop, Brit-Pop (lossless) - 202, // Indie Rock, Indie Pop, Dream Pop, Brit-Pop (lossy) - 466, // Synthwave, Spacesynth, Dreamwave, Retrowave, Outrun (lossless) - 465, // Synthwave, Spacesynth, Dreamwave, Retrowave, Outrun (lossy) -} - -var PopForumIDs = []int{ - 2495, // Отечественная поп-музыка (parent) - 2497, // Зарубежная поп-музыка (parent) - 425, // Популярная музыка России и стран бывшего СССР (lossless) - 424, // Популярная музыка России и стран бывшего СССР (lossy) - 429, // Зарубежная поп-музыка (lossless) - 428, // Зарубежная поп-музыка (lossy) - 1753, // Итальянская поп-музыка (lossless) - 735, // Итальянская поп-музыка (lossy) - 714, // Латиноамериканская поп-музыка (lossless) - 2232, // Латиноамериканская поп-музыка (lossy) - 1330, // Восточноазиатская поп-музыка (lossless) - 1331, // Восточноазиатская поп-музыка (lossy) - 1634, // Советская эстрада, ретро, романсы (lossless) - 1635, // Советская эстрада, ретро, романсы (lossy) - 1361, // Популярная музыка России и стран бывшего СССР (сборники) (lossy) - 1362, // Зарубежная поп-музыка (сборники) (lossy) - 2270, // Easy Listening, Instrumental Pop (lossless) - 2275, // Easy Listening, Instrumental Pop (lossy) -} - -var ElectronicForumIDs = []int{ - 1807, // House, Techno, Hardcore, Hardstyle, Jumpstyle (parent) - 1808, // Drum & Bass, Jungle, Breakbeat, Dubstep, IDM, Electro (parent) - 1809, // Chillout, Lounge, Downtempo, Trip-Hop (parent) - 1810, // Traditional Electronic, Ambient, Modern Classical, Electroacoustic, Experimental (parent) - 1811, // Industrial, Noise, EBM, Dark Electro, Aggrotech, Cyberpunk, Synthpop, New Wave (parent) - 1821, // Trance, Goa Trance, Psy-Trance, PsyChill, Ambient, Dub (parent) - 2499, // Eurodance, Disco, Hi-NRG (parent) - 797, // Electro, Electro-Freestyle, Nu Electro (lossless) - 1805, // Electro, Electro-Freestyle, Nu Electro (lossy) - 1857, // House (lossless) - 1858, // House (lossy) - 1860, // House (Singles, EPs) (lossy) - 840, // House (Проморелизы, сборники) (lossy) - 1825, // Techno (lossless) - 1826, // Techno (lossy) - 1828, // Techno (Singles, EPs) (lossy) - 1829, // Hardcore, Hardstyle, Jumpstyle (lossless) - 1830, // Hardcore, Hardstyle, Jumpstyle (lossy) - 1831, // Hardcore, Hardstyle, Jumpstyle (vinyl, web) - 1832, // Drum & Bass, Jungle (lossless) - 1833, // Drum & Bass, Jungle (lossy) - 1836, // Breakbeat (lossless) - 1837, // Breakbeat (lossy) - 1839, // Dubstep (lossless) - 454, // Dubstep (lossy) - 1840, // IDM (lossless) - 1841, // IDM (lossy) - 2229, // IDM Discography & Collections (lossy) - 1818, // Trance (lossless) - 1819, // Trance (lossy) - 1847, // Trance (Singles, EPs) (lossy) - 1844, // Goa Trance, Psy-Trance (lossless) - 1822, // Goa Trance, Psy-Trance (lossy) - 1894, // PsyChill, Ambient, Dub (lossless) - 1895, // PsyChill, Ambient, Dub (lossy) - 1861, // Chillout, Lounge, Downtempo (lossless) - 1862, // Chillout, Lounge, Downtempo (lossy) - 1945, // Trip Hop, Abstract Hip-Hop (lossless) - 1944, // Trip Hop, Abstract Hip-Hop (lossy) - 1864, // Traditional Electronic, Ambient (lossless) - 1865, // Traditional Electronic, Ambient (lossy) - 1871, // Modern Classical, Electroacoustic (lossless) - 1867, // Modern Classical, Electroacoustic (lossy) - 1869, // Experimental (lossless) - 1873, // Experimental (lossy) - 1866, // Darkwave, Neoclassical, Ethereal, Dungeon Synth (lossless) - 406, // Darkwave, Neoclassical, Ethereal, Dungeon Synth (lossy) - 1868, // EBM, Dark Electro, Aggrotech (lossless) - 1875, // EBM, Dark Electro, Aggrotech (lossy) - 1877, // Industrial, Noise (lossless) - 1878, // Industrial, Noise (lossy) - 1880, // Synthpop, Futurepop, New Wave, Electropop (lossless) - 1881, // Synthpop, Futurepop, New Wave, Electropop (lossy) - 1907, // Cyberpunk, 8-bit, Chiptune (lossy & lossless) - 2500, // Disco, Italo-Disco, Euro-Disco, Hi-NRG (lossless) - 2501, // Disco, Italo-Disco, Euro-Disco, Hi-NRG (lossy) - 2502, // Eurodance, Euro-House, Technopop (lossless) - 2503, // Eurodance, Euro-House, Technopop (lossy) - 2504, // Eurodance, Euro-House, Technopop (сборники) (lossy) - 2505, // Disco, Italo-Disco, Euro-Disco, Hi-NRG (сборники) (lossy) -} - -var HipHopForumIDs = []int{ - 408, // Рэп, Хип-Хоп, R'n'B (parent) - 441, // Отечественный Рэп, Хип-Хоп (lossy) - 1486, // Отечественный Рэп, Хип-Хоп, R'n'B (lossless) - 446, // Зарубежный Рэп, Хип-Хоп (lossy) - 909, // Зарубежный Рэп, Хип-Хоп (lossless) - 1665, // Зарубежный R'n'B (lossless) - 1172, // Зарубежный R'n'B (lossy) - 1173, // Отечественный R'n'B (lossy) - 2283, // Funk, Soul, R&B (lossless) -} - -var JazzForumIDs = []int{ - 2267, // Зарубежный джаз (parent) - 2269, // Отечественный джаз и блюз (parent) - 2277, // Early Jazz, Swing, Gypsy (lossless) - 2278, // Bop (lossless) - 2279, // Mainstream Jazz, Cool (lossless) - 2280, // Jazz Fusion (lossless) - 2281, // World Fusion, Ethnic Jazz (lossless) - 2282, // Avant-Garde Jazz, Free Improvisation (lossless) - 2284, // Smooth, Jazz-Pop (lossless) - 2285, // Vocal Jazz (lossless) - 2286, // Сборники зарубежного джаза (lossless) - 2287, // Зарубежный джаз (lossy) - 2353, // Modern Creative, Third Stream (lossless) - 1947, // Nu Jazz, Acid Jazz, Future Jazz (lossless) - 1946, // Nu Jazz, Acid Jazz, Future Jazz (lossy) - 2297, // Отечественный джаз (lossless) - 2295, // Отечественный джаз (lossy) -} - -var BluesForumIDs = []int{ - 2268, // Зарубежный блюз (parent) - 2290, // Roots, Pre-War Blues, Early R&B, Gospel (lossless) - 2292, // Blues-rock (lossless) - 2293, // Blues (Texas, Chicago, Modern and Others) (lossless) - 2288, // Зарубежный блюз (lossy) - 2289, // Зарубежный блюз (сборники; Tribute VA) (lossless) - 2296, // Отечественный блюз (lossless) - 2298, // Отечественный блюз (lossy) -} - -var ClassicalForumIDs = []int{ - 409, // Классическая и современная академическая музыка (parent) - 556, // Вокальная музыка (lossless) - 557, // Оркестровая музыка (lossless) - 558, // Камерная инструментальная музыка (lossless) - 793, // Сольная инструментальная музыка (lossless) - 794, // Опера (lossless) - 560, // Полные собрания сочинений и многодисковые издания (lossless) - 436, // Полные собрания сочинений и многодисковые издания (lossy) - 2307, // Хоровая музыка (lossless) - 2308, // Концерт для инструмента с оркестром (lossless) - 2309, // Вокальная и хоровая музыка (lossy) - 2310, // Оркестровая музыка (lossy) - 2311, // Камерная и сольная инструментальная музыка (lossy) - 969, // Классика в современной обработке, Classical Crossover (lossy и lossless) -} - -var FolkForumIDs = []int{ - 1125, // Фольклор, Народная и Этническая музыка (parent) - 1127, // New Age & Meditative (lossless) - 1126, // New Age & Meditative (lossy) - 1129, // Этническая музыка Сибири, Средней и Восточной Азии (lossless) - 1128, // Этническая музыка Сибири, Средней и Восточной Азии (lossy) - 1131, // Восточноевропейский фолк (lossless) - 1130, // Восточноевропейский фолк (lossy) - 1133, // Западноевропейский фолк (lossless) - 1132, // Западноевропейский фолк (lossy) - 1135, // Фламенко и акустическая гитара (lossless) - 1134, // Фламенко и акустическая гитара (lossy) - 1137, // Country, Bluegrass (lossless) - 1136, // Country, Bluegrass (lossy) - 1138, // Этническая музыка Австралии, Тихого и Индийского океанов (lossy и lossless) - 1282, // Фольклорная, Народная, Эстрадная музыка Кавказа и Закавказья (lossy и lossless) - 2085, // Этническая музыка Африки и Ближнего Востока (lossless) - 1283, // Этническая музыка Африки и Ближнего Востока (lossy) - 1285, // Этническая музыка Северной и Южной Америки (lossless) - 1284, // Этническая музыка Северной и Южной Америки (lossy) - 1856, // Этническая музыка Индии (lossy) - 2430, // Этническая музыка Индии (lossless) - 2084, // Klezmer и Еврейский фольклор (lossy и lossless) -} - -var ReggaeForumIDs = []int{ - 1760, // Reggae, Ska, Dub (parent) - 1764, // Rocksteady, Early Reggae, Ska-Jazz, Trad.Ska (lossy и lossless) - 1765, // Reggae (lossy) - 1768, // Reggae, Dancehall, Dub (lossless) - 1769, // Ska-Punk, Ska-Core (lossy) - 1770, // Dancehall, Raggamuffin (lossy) - 1771, // Dub (lossy) - 1772, // Отечественный Reggae, Ska, Dub (lossy и lossless) - 1774, // Ska, Ska-Punk, Ska-Jazz (lossless) - 1767, // 3rd Wave Ska (lossy) - 2233, // Reggae, Ska, Dub (компиляции) (lossy и lossless) -} - -var SoundtrackForumIDs = []int{ - 416, // Саундтреки, караоке и мюзиклы (parent) - 691, // Саундтреки к отечественным фильмам (lossless) - 469, // Саундтреки к отечественным фильмам (lossy) - 786, // Саундтреки к зарубежным фильмам (lossless) - 785, // Саундтреки к зарубежным фильмам (lossy) - 784, // Саундтреки к играм (lossless) - 783, // Саундтреки к играм (lossy) - 715, // Саундтреки к мультфильмам (lossy и lossless) - 1631, // Саундтреки к сериалам (lossless) - 1499, // Саундтреки к сериалам (lossy) - 1388, // Саундтреки к аниме (lossless) - 282, // Саундтреки к аниме (lossy) - 796, // Неофициальные саундтреки к фильмам и сериалам (lossy) - 2331, // Неофициальные саундтреки к играм (lossy) - 2431, // Аранжировки музыки из игр (lossy и lossless) - 880, // Мюзикл (lossy и lossless) -} - -var ShansonForumIDs = []int{ - 1215, // Шансон, Авторская и Военная песня (parent) - 1220, // Отечественный шансон (lossless) - 1221, // Отечественный шансон (lossy) - 1452, // Зарубежный шансон (lossless) - 1219, // Зарубежный шансон (lossy) - 1216, // Военная песня, марши (lossless) - 1223, // Военная песня, марши (lossy) - 1224, // Авторская песня (lossless) - 1225, // Авторская песня (lossy) - 1226, // Менестрели и ролевики (lossy и lossless) - 1334, // Сборники отечественного шансона (lossy) -} - -var HiResForumIDs = []int{ - 1299, // Hi-Res stereo и многоканальная музыка (parent) - 1755, // Рок-музыка (Hi-Res stereo) - 1757, // Рок-музыка (многоканальная музыка) - 1884, // Классика и классика в современной обработке (Hi-Res stereo) - 1164, // Классика и классика в современной обработке (многоканальная музыка) - 1885, // Поп-музыка (Hi-Res stereo) - 1163, // Поп-музыка (многоканальная музыка) - 1893, // Электронная музыка (Hi-Res stereo) - 1890, // Электронная музыка (многоканальная музыка) - 2302, // Джаз и Блюз (Hi-Res stereo) - 2303, // Джаз и Блюз (многоканальная музыка) - 1397, // Саундтреки (Hi-Res stereo и многоканальная музыка) - 2512, // Музыка разных жанров (Hi-Res stereo и многоканальная музыка) - 2513, // New Age, Relax, Meditative & Flamenco (Hi-Res stereo и многоканальная музыка) - 1170, // Конверсии SACD - 453, // Конверсии Quadraphonic - 1759, // Конверсии Blu-Ray, ADVD и DVD-Audio - 1852, // Апмиксы-Upmixes - 860, // Неофициальные конверсии цифровых форматов (parent) -} - -var DigitizationForumIDs = []int{ - 2219, // Оцифровки с аналоговых носителей (parent) - 239, // Отечественная поп-музыка (оцифровки) - 1444, // Зарубежная поп-музыка (оцифровки) - 450, // Инструментальная поп-музыка (оцифровки) - 1756, // Зарубежная рок-музыка (оцифровки) - 1758, // Отечественная рок-музыка (оцифровки) - 1754, // Электронная музыка (оцифровки) - 1660, // Классика и классика в современной обработке (оцифровки) - 506, // Фольклор, народная и этническая музыка (оцифровки) - 1835, // Rap, Hip-Hop, R'n'B, Reggae, Ska, Dub (оцифровки) - 2301, // Джаз и блюз (оцифровки) - 1217, // Шансон, авторские, военные песни и марши (оцифровки) - 1625, // Саундтреки и мюзиклы (оцифровки) - 2401, // Советская эстрада, ретро, романсы (оцифровки) - 974, // Музыка других жанров (оцифровки) -} - -var LabelPackForumIDs = []int{ - 782, // Лейбл- и сцен-паки (parent) - 1842, // Лейбл-паки (lossless) - 1648, // Лейбл-паки, Сцен-паки (lossy) - 134, // Неофициальные сборники и ремастеринги (lossless) - 965, // Неофициальные сборники (lossy) - 577, // AI-Music (lossy и lossless) - 2230, // Сборники (lossless) - 2231, // Сборники (lossy) -} - -var RadioshowForumIDs = []int{ - 1859, // House (Radioshow, Podcast, Liveset, Mixes) - 1824, // Trance (Radioshows, Podcasts, Live Sets, Mixes) (lossy) - 1827, // Techno (Radioshows, Podcasts, Livesets, Mixes) - 1834, // Drum & Bass, Jungle (Radioshows, Podcasts, Livesets, Mixes) - 1838, // Breakbeat, Dubstep (Radioshows, Podcasts, Livesets, Mixes) - 460, // Goa Trance, Psy-Trance, PsyChill, Ambient, Dub (Live Sets, Mixes) (lossy) -} - -var AACForumIDs = []int{ - 2240, // Музыка Lossy (AAC-iTunes) - 2244, // Музыка Lossy (AAC) (Singles, EPs) - 2248, // Музыка Lossy (AAC) - 1927, // Музыка lossless (ALAC) -} - -var MiscMusicForumIDs = []int{ - 1395, // Духовные песнопения и музыка (lossless) - 1396, // Духовные песнопения и музыка (lossy) - 1351, // Сборники песен для детей (lossy и lossless) - 2018, // Музыка для бальных танцев (lossy и lossless) - 855, // Звуки природы - 1929, // Смешанные стили -} - -var AllMusicForumIDs = concat( - RockForumIDs, - MetalForumIDs, - AlternativeForumIDs, - PopForumIDs, - ElectronicForumIDs, - HipHopForumIDs, - JazzForumIDs, - BluesForumIDs, - ClassicalForumIDs, - FolkForumIDs, - ReggaeForumIDs, - SoundtrackForumIDs, - ShansonForumIDs, - HiResForumIDs, - DigitizationForumIDs, - LabelPackForumIDs, - RadioshowForumIDs, - AACForumIDs, - MiscMusicForumIDs, -) - -func concat(slices ...[]int) []int { - var result []int - for _, s := range slices { - result = append(result, s...) - } - return result -} diff --git a/internal/tracker/rutracker/factory.go b/internal/tracker/rutracker/factory.go deleted file mode 100644 index 086bd91..0000000 --- a/internal/tracker/rutracker/factory.go +++ /dev/null @@ -1,118 +0,0 @@ -package rutracker - -import ( - "strconv" - - "homelab.lan/music-agregator/internal/tracker" - "homelab.lan/music-agregator/internal/tracker/rutracker/parser" -) - -type parserType int - -const ( - parserGeneral parserType = iota - parserRock - parserMetal - parserAlternative - parserPop - parserElectronic - parserHipHop - parserJazz - parserBlues - parserClassical - parserFolk - parserReggae - parserSoundtracks - parserShanson - parserHiRes - parserDigitization - parserLabelPacks - parserRadioshow - parserAAC - parserMiscMusic -) - -var categoryToParser map[int]parserType - -func init() { - categoryToParser = make(map[int]parserType) - - categoryToParser[3000] = parserGeneral - categoryToParser[3010] = parserGeneral - categoryToParser[3040] = parserGeneral - - registerAll(RockForumIDs, parserRock) - registerAll(MetalForumIDs, parserMetal) - registerAll(AlternativeForumIDs, parserAlternative) - registerAll(PopForumIDs, parserPop) - registerAll(ElectronicForumIDs, parserElectronic) - registerAll(HipHopForumIDs, parserHipHop) - registerAll(JazzForumIDs, parserJazz) - registerAll(BluesForumIDs, parserBlues) - registerAll(ClassicalForumIDs, parserClassical) - registerAll(FolkForumIDs, parserFolk) - registerAll(ReggaeForumIDs, parserReggae) - registerAll(SoundtrackForumIDs, parserSoundtracks) - registerAll(ShansonForumIDs, parserShanson) - registerAll(HiResForumIDs, parserHiRes) - registerAll(DigitizationForumIDs, parserDigitization) - registerAll(LabelPackForumIDs, parserLabelPacks) - registerAll(RadioshowForumIDs, parserRadioshow) - registerAll(AACForumIDs, parserAAC) - registerAll(MiscMusicForumIDs, parserMiscMusic) -} - -func registerAll(ids []int, pt parserType) { - for _, id := range ids { - categoryToParser[id] = pt - } -} - -type ParserFactory struct { - parsers map[parserType]tracker.Parser -} - -func NewRuTrackerParserFactory() *ParserFactory { - return &ParserFactory{ - parsers: map[parserType]tracker.Parser{ - parserGeneral: parser.NewGeneralParser(), - parserRock: parser.NewRockParser(), - parserMetal: parser.NewMetalParser(), - parserAlternative: parser.NewAlternativeParser(), - parserPop: parser.NewPopParser(), - parserElectronic: parser.NewElectronicParser(), - parserHipHop: parser.NewHipHopParser(), - parserJazz: parser.NewJazzParser(), - parserBlues: parser.NewBluesParser(), - parserClassical: parser.NewClassicalParser(), - parserFolk: parser.NewFolkParser(), - parserReggae: parser.NewReggaeParser(), - parserSoundtracks: parser.NewSoundtracksParser(), - parserShanson: parser.NewShansonParser(), - parserHiRes: parser.NewHiResParser(), - parserDigitization: parser.NewVinylDigitizationParser(), - parserLabelPacks: parser.NewLabelPacksParser(), - parserRadioshow: parser.NewRadioshowParser(), - parserAAC: parser.NewAACParser(), - parserMiscMusic: parser.NewMiscMusicParser(), - }, - } -} - -const jackettIDOffset = 100000 - -func (f *ParserFactory) GetParser(categories []string) tracker.Parser { - for _, cat := range categories { - catID, err := strconv.Atoi(cat) - if err != nil { - continue - } - if catID >= jackettIDOffset { - catID -= jackettIDOffset - } - if pt, ok := categoryToParser[catID]; ok { - return f.parsers[pt] - } - } - return f.parsers[parserGeneral] -} diff --git a/internal/tracker/rutracker/factory_test.go b/internal/tracker/rutracker/factory_test.go deleted file mode 100644 index 27c6729..0000000 --- a/internal/tracker/rutracker/factory_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package rutracker - -import ( - "testing" - - "homelab.lan/music-agregator/internal/tracker" - "homelab.lan/music-agregator/internal/tracker/rutracker/parser" -) - -func TestParserFactory_GetParser(t *testing.T) { - f := NewRuTrackerParserFactory() - - tests := []struct { - name string - categories []string - wantType string - }{ - {"torznab general 3000", []string{"3000"}, "*parser.GeneralParser"}, - {"torznab general 3010", []string{"3010"}, "*parser.GeneralParser"}, - {"torznab general 3040", []string{"3040"}, "*parser.GeneralParser"}, - {"rock forum", []string{"1702"}, "*parser.RockParser"}, - {"metal forum raw id", []string{"1728"}, "*parser.MetalParser"}, - {"metal forum jackett id", []string{"101728"}, "*parser.MetalParser"}, - {"alternative forum", []string{"464"}, "*parser.AlternativeParser"}, - {"pop forum", []string{"425"}, "*parser.PopParser"}, - {"electronic forum", []string{"1857"}, "*parser.ElectronicParser"}, - {"hiphop forum", []string{"909"}, "*parser.HipHopParser"}, - {"jazz forum", []string{"2277"}, "*parser.JazzParser"}, - {"blues forum", []string{"2292"}, "*parser.BluesParser"}, - {"classical forum", []string{"556"}, "*parser.ClassicalParser"}, - {"folk forum", []string{"1127"}, "*parser.FolkParser"}, - {"reggae forum", []string{"1768"}, "*parser.ReggaeParser"}, - {"soundtrack forum", []string{"786"}, "*parser.SoundtracksParser"}, - {"shanson forum", []string{"1220"}, "*parser.ShansonParser"}, - {"hires forum", []string{"1755"}, "*parser.HiResParser"}, - {"digitization forum", []string{"239"}, "*parser.VinylDigitizationParser"}, - {"label pack forum", []string{"1842"}, "*parser.LabelPacksParser"}, - {"radioshow forum", []string{"1859"}, "*parser.RadioshowParser"}, - {"aac forum", []string{"2240"}, "*parser.AACParser"}, - {"misc music forum", []string{"1395"}, "*parser.MiscMusicParser"}, - {"unknown category falls back to general", []string{"99999"}, "*parser.GeneralParser"}, - {"empty categories falls back to general", []string{}, "*parser.GeneralParser"}, - {"multiple categories uses first match", []string{"99999", "1728"}, "*parser.MetalParser"}, - {"jackett prefixed id stripped", []string{"101719"}, "*parser.MetalParser"}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - p := f.GetParser(tt.categories) - gotType := getParserTypeName(p) - if gotType != tt.wantType { - t.Errorf("GetParser(%v) = %v, want %v", tt.categories, gotType, tt.wantType) - } - }) - } -} - -func getParserTypeName(p tracker.Parser) string { - switch p.(type) { - case *parser.GeneralParser: - return "*parser.GeneralParser" - case *parser.LosslessParser: - return "*parser.LosslessParser" - case *parser.LossyParser: - return "*parser.LossyParser" - case *parser.HiResParser: - return "*parser.HiResParser" - case *parser.VinylDigitizationParser: - return "*parser.VinylDigitizationParser" - case *parser.ClassicalParser: - return "*parser.ClassicalParser" - case *parser.JazzParser: - return "*parser.JazzParser" - case *parser.MetalParser: - return "*parser.MetalParser" - case *parser.SoundtracksParser: - return "*parser.SoundtracksParser" - case *parser.DiscographyParser: - return "*parser.DiscographyParser" - case *parser.LabelPacksParser: - return "*parser.LabelPacksParser" - case *parser.RockParser: - return "*parser.RockParser" - case *parser.AlternativeParser: - return "*parser.AlternativeParser" - case *parser.PopParser: - return "*parser.PopParser" - case *parser.ElectronicParser: - return "*parser.ElectronicParser" - case *parser.HipHopParser: - return "*parser.HipHopParser" - case *parser.BluesParser: - return "*parser.BluesParser" - case *parser.FolkParser: - return "*parser.FolkParser" - case *parser.ReggaeParser: - return "*parser.ReggaeParser" - case *parser.ShansonParser: - return "*parser.ShansonParser" - case *parser.RadioshowParser: - return "*parser.RadioshowParser" - case *parser.AACParser: - return "*parser.AACParser" - case *parser.MiscMusicParser: - return "*parser.MiscMusicParser" - default: - return "unknown" - } -} diff --git a/internal/tracker/rutracker/filter.go b/internal/tracker/rutracker/filter.go deleted file mode 100644 index 9f7b4a3..0000000 --- a/internal/tracker/rutracker/filter.go +++ /dev/null @@ -1,25 +0,0 @@ -package rutracker - -import "strconv" - -type Filter struct{} - -func NewFilter() *Filter { - return &Filter{} -} - -func (f *Filter) IsKnownCategory(categories []string) bool { - for _, cat := range categories { - catID, err := strconv.Atoi(cat) - if err != nil { - continue - } - if catID >= jackettIDOffset { - catID -= jackettIDOffset - } - if _, ok := categoryToParser[catID]; ok { - return true - } - } - return false -} diff --git a/internal/tracker/rutracker/filter_test.go b/internal/tracker/rutracker/filter_test.go deleted file mode 100644 index cab079a..0000000 --- a/internal/tracker/rutracker/filter_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package rutracker - -import "testing" - -func TestFilter_IsKnownCategory(t *testing.T) { - f := NewFilter() - - tests := []struct { - name string - categories []string - want bool - }{ - {"torznab lossless", []string{"3040"}, true}, - {"torznab lossy", []string{"3010"}, true}, - {"torznab general audio", []string{"3000"}, true}, - {"rutracker pop forum", []string{"425"}, true}, - {"rutracker hires forum", []string{"1755"}, true}, - {"rutracker metal forum", []string{"1728"}, true}, - {"jackett prefixed id", []string{"101728"}, true}, - {"unknown category", []string{"99999"}, false}, - {"empty categories", []string{}, false}, - {"books category", []string{"7000"}, false}, - {"mixed known and unknown", []string{"99999", "3040"}, true}, - {"invalid non-numeric", []string{"abc"}, false}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := f.IsKnownCategory(tt.categories) - if got != tt.want { - t.Errorf("IsKnownCategory(%v) = %v, want %v", tt.categories, got, tt.want) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/aac.go b/internal/tracker/rutracker/parser/aac.go deleted file mode 100644 index fad8a10..0000000 --- a/internal/tracker/rutracker/parser/aac.go +++ /dev/null @@ -1,37 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type AACParser struct { - BaseParser -} - -func NewAACParser() *AACParser { - return &AACParser{} -} - -func (p *AACParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - if len(r.Genres) == 0 { - } - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/aac_test.go b/internal/tracker/rutracker/parser/aac_test.go deleted file mode 100644 index 6812a5d..0000000 --- a/internal/tracker/rutracker/parser/aac_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestAACParser(t *testing.T) { - p := NewAACParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantBitrate string - wantParseOK bool - }{ - { - name: "Pop AAC VBR", - title: "(Pop) Zivert - Айсберг (Apple Music Home Session) - 2022, AAC (tracks), VBR 256 kbps", - wantArtist: "Zivert", - wantYear: 2022, - wantFormat: release.FormatAAC, - wantParseOK: true, - }, - { - name: "OST ALAC CD", - title: "(OST) [CD] Rockstar Games Presents Music From And Inspired By Grand Theft Auto IV: Vladivostok FM - 2008, ALAC (tracks+.cue), lossless", - wantArtist: "Rockstar Games Presents Music From And Inspired By Grand Theft Auto IV: Vladivostok FM", - wantYear: 2008, - wantFormat: release.FormatALAC, - wantType: release.TypeSoundtrack, - wantParseOK: true, - }, - { - name: "Hip-hop ALAC discography", - title: "(Hip-Hop, rap rock, hardcore rap, chopper) [CD`39|WEB`6] [Strange Music] Tech N9ne - Дискография / Discography - 1999-2025, ALAC (tracks+.cue), lossless", - wantArtist: "Tech N9ne", - wantYear: 1999, - wantFormat: release.FormatALAC, - wantType: release.TypeDiscography, - wantParseOK: true, - }, - { - name: "Art rock iTunes AAC", - title: "(Art Rock / Pop Rock) Roxy Music - Дискография / iTunes Discography - 1972-2004 [WEB], AAC (tracks), 256 kbps", - wantArtist: "Roxy Music", - wantYear: 1972, - wantFormat: release.FormatAAC, - wantType: release.TypeDiscography, - wantBitrate: "256 kbps", - wantParseOK: true, - }, - { - name: "Jazz AAC 320", - title: "(Jazz, Post-Bop, Modal Music) Masaru Imada + Kenji Kohsei Quartet - All Of A Glow (Hiroshi Murakami, Kenji Kosei, Masaru Imada, Nobuyoshi Ino) - 1978, AAC (tracks), 320 kbps", - wantArtist: "Masaru Imada + Kenji Kohsei Quartet", - wantYear: 1978, - wantFormat: release.FormatAAC, - wantBitrate: "320 kbps", - wantParseOK: true, - }, - { - name: "Rock pop ALAC digital master", - title: "(Rock Pop) [WEB] Bryan Adams - Ultimate [Apple Music Digital Master] {24-44.1} - 2017, ALAC (tracks), lossless", - wantArtist: "Bryan Adams", - wantYear: 2017, - wantFormat: release.FormatALAC, - wantParseOK: true, - }, - { - name: "Alternative electronic VA AAC", - title: "(Alternative, Electronic) VA - Astralwerks - Music In 20/20 (Feat. The Chemical Brothers, Doves, Swedish House Mafia, Air, Diamond Rings, Hot Chip, Kings Of Convenience, The Kooks, Kraftwerk & more) - 2013, AAC (tracks), TVBR q127", - wantArtist: "VA", - wantYear: 2013, - wantFormat: release.FormatAAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Eurodance ALAC multi-CD", - title: "(EuroHouse, EuroDance, Other) [CD] VA - Promotion Dance Hits (Snake's Music) (22 CD), 1994-1996, ALAC, (tracks+.cue), lossless [не flac]", - wantArtist: "VA", - wantYear: 1994, - wantFormat: release.FormatALAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Lounge chill jazz christmas AAC", - title: "(Lounge, Chill Out, Jazz) VA - Christmas Jazz Night 1-7 (Best X-Mas Jazz Music) - 2017-2023, AAC (tracks), TVBR q127 (WEB)", - wantArtist: "VA", - wantYear: 2017, - wantFormat: release.FormatAAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Electro house dance AAC", - title: "(Electro, House, Dance) VA - Music & Fashion (The Deep-House Shows), Vol. 1-4 - 2023, AAC (tracks), TVBR q127 (WEB)", - wantArtist: "VA", - wantYear: 2023, - wantFormat: release.FormatAAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/alternative.go b/internal/tracker/rutracker/parser/alternative.go deleted file mode 100644 index 1f3f9d4..0000000 --- a/internal/tracker/rutracker/parser/alternative.go +++ /dev/null @@ -1,38 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type AlternativeParser struct { - BaseParser -} - -func NewAlternativeParser() *AlternativeParser { - return &AlternativeParser{} -} - -func (p *AlternativeParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - if len(r.Genres) == 0 { - r.Genres = []string{"Alternative"} - } - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/alternative_test.go b/internal/tracker/rutracker/parser/alternative_test.go deleted file mode 100644 index 8a7c493..0000000 --- a/internal/tracker/rutracker/parser/alternative_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestAlternativeParser(t *testing.T) { - p := NewAlternativeParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantBitrate string - wantParseOK bool - }{ - { - name: "Nu-metal album", - title: "(Nu-Metal, Alternative Metal) [WEB] Korn - Reward the Scars - 2026, FLAC (tracks), lossless", - wantArtist: "Korn", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Punk rock discography", - title: "(Punk Rock / Alternative Rock) [CD / WEB] Bayside - Дискография - 2001-2025, (21 CD), FLAC (tracks+cue, tracks), lossless", - wantArtist: "Bayside", - wantYear: 2001, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Alternative metal discography", - title: "(Alternative Metal / Nu Metal) [CD / WEB] Sevendust - Дискография - 1997-2026, (35 CD), FLAC (tracks+cue, tracks), lossless", - wantArtist: "Sevendust", - wantYear: 1997, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Alt rock female vocals", - title: "(Alt. Rock / Alternative Metal / Female Vocals) [WEB] EarlyRise - The Flood Is Coming - 2026, FLAC (tracks), lossless", - wantArtist: "EarlyRise", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Alternative rock electronic", - title: "(Alternative Rock / Post-Hardcore / Electronic) [WEB] Nvtures Ghost - I Have No Moth And I Must Scream - 2026, FLAC (tracks), lossless", - wantArtist: "Nvtures Ghost", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Alternative discography", - title: "(Alternative) [WEB] KSB muzic - Дискография - 2022-2025, FLAC (tracks), lossless", - wantArtist: "KSB muzic", - wantYear: 2022, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Russian indie discography", - title: "(Russian Indie, Indie, Rock, Punk, Alternative,) [WEB] Полматери -Дискография (15 релизов) - 2019-2026, FLAC (tracks), lossless", - wantArtist: "Полматери", - wantYear: 2019, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Britpop discography", - title: "(Britpop / Alternative Rock / Indie Rock) [CD / WEB] elbow - Дискография - 2001-2025, (51 CD), FLAC (tracks+cue, tracks), lossless", - wantArtist: "elbow", - wantYear: 2001, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Alternative rock CD", - title: "(Alternative Rock) [CD] Foo Fighters - Your Favorite Toy - 2026, FLAC (tracks+.cue), lossless", - wantArtist: "Foo Fighters", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Smashing Pumpkins multi-genre", - title: "(Alternative Rock, Shoegaze, Noise Rock, Dream Pop, Alternative Metal) [CD] The Smashing Pumpkins - Machina II: The Friends & Enemies Of Modern Music (Q101) - 2000 (2 CD), FLAC (tracks+.cue), lossless", - wantArtist: "The Smashing Pumpkins", - wantYear: 2000, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/base.go b/internal/tracker/rutracker/parser/base.go deleted file mode 100644 index b6e032d..0000000 --- a/internal/tracker/rutracker/parser/base.go +++ /dev/null @@ -1,282 +0,0 @@ -package parser - -import ( - "strconv" - "strings" - - "homelab.lan/music-agregator/internal/release" -) - -type BaseParser struct{} - -func (p *BaseParser) NewRelease(title string) *release.Release { - return &release.Release{ - RawTitle: title, - ParsedSuccessfully: true, - } -} - -func (p *BaseParser) ExtractGenres(title string) []string { - match := genrePattern.FindStringSubmatch(title) - if len(match) < 2 { - return nil - } - raw := match[1] - parts := strings.FieldsFunc(raw, func(r rune) bool { - return r == ',' || r == '/' || r == ';' - }) - var genres []string - for _, part := range parts { - trimmed := strings.TrimSpace(part) - if trimmed != "" { - genres = append(genres, trimmed) - } - } - return genres -} - -func (p *BaseParser) StripGenrePrefix(title string) string { - return genrePattern.ReplaceAllString(title, "") -} - -func (p *BaseParser) StripLeadingTags(title string) string { - return leadingTagsPattern.ReplaceAllString(title, "") -} - -func (p *BaseParser) ExtractYear(title string) int { - if match := reissueYearPattern.FindStringSubmatch(title); len(match) >= 2 { - year, _ := strconv.Atoi(match[1]) - return year - } - - if match := releaseYearPattern.FindStringSubmatch(title); len(match) >= 2 { - year, _ := strconv.Atoi(match[1]) - return year - } - - match := yearPattern.FindStringSubmatch(title) - if len(match) < 2 { - return 0 - } - year, _ := strconv.Atoi(match[1]) - return year -} - -func (p *BaseParser) ExtractYearRange(title string) (int, int) { - if match := releaseYearPattern.FindStringSubmatch(title); len(match) >= 2 { - year, _ := strconv.Atoi(match[1]) - return year, 0 - } - - if match := reissueYearPattern.FindStringSubmatch(title); len(match) >= 2 { - year, _ := strconv.Atoi(match[1]) - return year, 0 - } - - rangeMatch := yearRangePattern.FindStringSubmatch(title) - if len(rangeMatch) >= 3 { - start, _ := strconv.Atoi(rangeMatch[1]) - end, _ := strconv.Atoi(rangeMatch[2]) - return start, end - } - - match := yearPattern.FindStringSubmatch(title) - if len(match) >= 2 { - year, _ := strconv.Atoi(match[1]) - return year, 0 - } - return 0, 0 -} - -func (p *BaseParser) ExtractFormat(title string) release.AudioFormat { - match := formatPattern.FindStringSubmatch(title) - if len(match) < 2 { - return release.FormatUnknown - } - format := strings.ToUpper(match[1]) - switch { - case format == "FLAC": - return release.FormatFLAC - case format == "MP3": - return release.FormatMP3 - case format == "AAC": - return release.FormatAAC - case format == "APE": - return release.FormatAPE - case format == "WV" || format == "WAVPACK": - return release.FormatWavPack - case format == "ALAC": - return release.FormatALAC - case format == "OGG": - return release.FormatOGG - case format == "WAV": - return release.FormatWAV - default: - return release.FormatUnknown - } -} - -func (p *BaseParser) ExtractBitrate(title string) string { - if strings.Contains(strings.ToLower(title), "lossless") { - return "lossless" - } - match := bitratePattern.FindStringSubmatch(title) - if len(match) < 2 { - return "" - } - if match[1] != "" { - return match[1] + " kbps" - } - if match[2] != "" { - return "V" + match[2] - } - if match[3] != "" { - return "VBR ~" + match[3] + " kbps" - } - if match[4] != "" && match[5] != "" { - return "VBR " + match[4] + "-" + match[5] + " kbps" - } - return "" -} - -func (p *BaseParser) ExtractRipType(title string) string { - match := ripTypePattern.FindStringSubmatch(title) - if len(match) < 2 { - return "" - } - return strings.ToLower(match[1]) -} - -func (p *BaseParser) ExtractSource(title string) release.Source { - match := sourceTagPattern.FindStringSubmatch(title) - if len(match) < 2 { - if strings.Contains(strings.ToLower(title), "web") { - return release.SourceWEB - } - return release.SourceUnknown - } - tag := strings.ToUpper(match[1]) - switch tag { - case "CD": - return release.SourceCD - case "WEB": - return release.SourceWEB - case "LP", "VINYL", "MINI-LP", "EP", "12\"", "10\"", "7\"": - return release.SourceVinyl - case "SACD", "DVDA", "HDAD": - return release.SourceDVD - default: - return release.SourceUnknown - } -} - -func (p *BaseParser) ExtractHiRes(title string) (bitDepth int, sampleRate int) { - match := hiResPattern.FindStringSubmatch(title) - if len(match) < 3 { - return 0, 0 - } - bitDepth, _ = strconv.Atoi(match[1]) - sr := match[2] - if strings.Contains(sr, ".") { - f, _ := strconv.ParseFloat(sr, 64) - sampleRate = int(f * 1000) - } else { - sampleRate, _ = strconv.Atoi(sr) - sampleRate *= 1000 - } - return bitDepth, sampleRate -} - -func (p *BaseParser) ExtractSpecialTags(title string) []string { - matches := specialTagPattern.FindAllStringSubmatch(title, -1) - var tags []string - for _, match := range matches { - if len(match) >= 2 { - tags = append(tags, match[1]) - } - } - return tags -} - -func (p *BaseParser) ExtractReleaseCount(title string) int { - match := releaseCountPattern.FindStringSubmatch(title) - if len(match) < 2 { - return 0 - } - count, _ := strconv.Atoi(match[1]) - return count -} - -func (p *BaseParser) ExtractLabel(title string) string { - match := labelPattern.FindStringSubmatch(title) - if len(match) < 2 { - return "" - } - return strings.TrimSpace(match[1]) -} - -func (p *BaseParser) ExtractCatalogNum(title string) string { - match := catalogNumPattern.FindStringSubmatch(title) - if len(match) < 2 { - return "" - } - return match[1] -} - -func (p *BaseParser) DetectType(title string) release.Type { - switch { - case discographyPattern.MatchString(title): - return release.TypeDiscography - case collectionPattern.MatchString(title): - return release.TypeCollection - case bootlegPattern.MatchString(title): - return release.TypeBootleg - case anthologyPattern.MatchString(title): - return release.TypeCollection - case soundtrackPattern.MatchString(title): - return release.TypeSoundtrack - case livePattern.MatchString(title): - return release.TypeLive - case epPattern.MatchString(title): - return release.TypeEP - case singlePattern.MatchString(title): - return release.TypeSingle - case bestOfPattern.MatchString(title): - return release.TypeCompilation - case compilationPattern.MatchString(title): - return release.TypeCompilation - default: - return release.TypeAlbum - } -} - -func (p *BaseParser) ExtractArtistAlbum(title string) (artist string, album string) { - cleaned := tagsBeforeGenrePattern.ReplaceAllString(title, "") - cleaned = p.StripGenrePrefix(cleaned) - cleaned = p.StripLeadingTags(cleaned) - cleaned = trailingTechPattern.ReplaceAllString(cleaned, "") - - if match := standardTitlePattern.FindStringSubmatch(cleaned); len(match) >= 3 { - return strings.TrimSpace(match[1]), strings.TrimSpace(match[2]) - } - - if match := altTitlePattern.FindStringSubmatch(cleaned); len(match) >= 3 { - return strings.TrimSpace(match[1]), strings.TrimSpace(match[2]) - } - - parts := strings.SplitN(cleaned, " - ", 3) - if len(parts) >= 2 { - artist = strings.TrimSpace(parts[0]) - albumPart := strings.TrimSpace(parts[1]) - albumPart = yearPattern.ReplaceAllString(albumPart, "") - albumPart = strings.Trim(albumPart, " -–,") - album = albumPart - } - - return artist, album -} - -func (p *BaseParser) AddError(r *release.Release, err string) { - r.ParseErrors = append(r.ParseErrors, err) - r.ParsedSuccessfully = false -} diff --git a/internal/tracker/rutracker/parser/blues.go b/internal/tracker/rutracker/parser/blues.go deleted file mode 100644 index d1f0d09..0000000 --- a/internal/tracker/rutracker/parser/blues.go +++ /dev/null @@ -1,38 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type BluesParser struct { - BaseParser -} - -func NewBluesParser() *BluesParser { - return &BluesParser{} -} - -func (p *BluesParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - if len(r.Genres) == 0 { - r.Genres = []string{"Blues"} - } - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/blues_test.go b/internal/tracker/rutracker/parser/blues_test.go deleted file mode 100644 index 20f573e..0000000 --- a/internal/tracker/rutracker/parser/blues_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestBluesParser(t *testing.T) { - p := NewBluesParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantBitrate string - wantParseOK bool - }{ - { - name: "Blues rock classic rock reissue", - title: "(Blues Rock, Classic Rock) [CD] Rory Gallagher - Against the Grain - 2018 (1975), FLAC (image+.cue), lossless", - wantArtist: "Rory Gallagher", - wantYear: 2018, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Blues album WEB", - title: "(Blues) [WEB] Roger C. Wade & The Houserockers - Shake it loose! - 2026, FLAC (tracks), lossless", - wantArtist: "Roger C. Wade & The Houserockers", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Blues rock soldier", - title: "(Blues Rock) [WEB] Krissy Matthews - Rock and Roll Soldier - 2026, FLAC (tracks), lossless", - wantArtist: "Krissy Matthews", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Blues folk album", - title: "(Blues, Folk) [WEB] Gurf Morlix - Cobwebs & Stardust - 2026, FLAC (tracks), lossless", - wantArtist: "Gurf Morlix", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Blues dan penn", - title: "(Blues) [WEB] Dan Penn - Smoke Filled Room - 2026, FLAC (tracks), lossless", - wantArtist: "Dan Penn", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Blues rock paradise", - title: "(Blues Rock) [WEB] Catfish John Tisdell - Blues in Paradise - 2026, FLAC (tracks), lossless", - wantArtist: "Catfish John Tisdell", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Blues shades", - title: "(Blues) [WEB] Carrie Marshall - Shades of Blue - 2026, FLAC (tracks), lossless", - wantArtist: "Carrie Marshall", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Blues rock dont be mean", - title: "(Blues Rock) [WEB] Boogie Beasts - Don't Be So Mean! - 2026, FLAC (tracks), lossless", - wantArtist: "Boogie Beasts", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Blues against machine", - title: "(Blues) [WEB] Blues Against The Machine - VOL. II - 2026, FLAC (tracks), lossless", - wantArtist: "Blues Against The Machine", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Blues bon appetit", - title: "(Blues) [WEB] Andhrea and the Black Cats - Bon Appetit!! - 2026, FLAC (tracks), lossless", - wantArtist: "Andhrea and the Black Cats", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/classical.go b/internal/tracker/rutracker/parser/classical.go deleted file mode 100644 index 7936947..0000000 --- a/internal/tracker/rutracker/parser/classical.go +++ /dev/null @@ -1,38 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type ClassicalParser struct { - BaseParser -} - -func NewClassicalParser() *ClassicalParser { - return &ClassicalParser{} -} - -func (p *ClassicalParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - if len(r.Genres) == 0 { - r.Genres = []string{"Classical"} - } - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/classical_test.go b/internal/tracker/rutracker/parser/classical_test.go deleted file mode 100644 index 1c260e4..0000000 --- a/internal/tracker/rutracker/parser/classical_test.go +++ /dev/null @@ -1,130 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestClassicalParser(t *testing.T) { - p := NewClassicalParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantGenres []string - wantParseOK bool - }{ - { - name: "Rachmaninoff concerto", - title: "(Classical) [CD] Rachmaninoff - Piano Concerto No.3 - Nobuyuki Tsujii, Royal Liverpool Philharmonic Orchestra - 2026, FLAC (image+.cue) lossless", - wantArtist: "Rachmaninoff", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantType: release.TypeAlbum, - wantGenres: []string{"Classical"}, - wantParseOK: true, - }, - { - name: "Shostakovich symphonies collection", - title: "(Classical) [CD] Dmitry Shostakovich - Symphonies 1-15 (Boston Symphony Orchestra, Andris Nelsons) [19 CDs] - 2025, FLAC (image+.cue) lossless", - wantArtist: "Dmitry Shostakovich", - wantYear: 2025, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "TR24 OF Brahms symphonies", - title: "[TR24][OF] Brahms - The Complete Symphonies (Royal Concertgebouw Orchestra, John Eliot Gardiner) - 2025 (Classical)", - wantArtist: "Brahms", - wantYear: 2025, - wantParseOK: true, - }, - { - name: "Haitink complete recordings", - title: "(Classical) [CD] Bernard Haitink - Concertgebouworkest Edition Complete Studio Recordings [113 CDs] - 2022, FLAC (image+.cue) lossless", - wantArtist: "Bernard Haitink", - wantYear: 2022, - wantType: release.TypeCollection, - wantParseOK: true, - }, - { - name: "Tchaikovsky symphonies", - title: "(Classical) [CD] Чайковский - Complete 8 Symphonies plus Concertos [10 CDs] - 2024, FLAC (image+.cue) lossless", - wantArtist: "Чайковский", - wantYear: 2024, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Wagner opera TR24", - title: "[TR24][OF] Wagner - Siegfried (Symphonieorchester des Bayerischen Rundfunks, Sir Simon Rattle) - 2025 (Opera)", - wantArtist: "Wagner", - wantYear: 2025, - wantParseOK: true, - }, - { - name: "Strauss Elektra opera", - title: "[TR24][OF] Irène Theorin, Bergen Philharmonic Orchestra - R. Strauss Elektra Op. 58 - 2026 (Classical, Opera)", - wantYear: 2026, - wantParseOK: true, - }, - { - name: "Bruckner symphonies remaster", - title: "[TR24][OF] Bruckner - Symphonies Nos. 5 and 6 (New Philharmonia Orchestra, Otto Klemperer) - 2024 (Classical)", - wantArtist: "Bruckner", - wantYear: 2024, - wantParseOK: true, - }, - { - name: "DSD Brahms chamber music", - title: "[DSD][OF] The Brahms Project - Brahms The Complete Piano Quartets - 2017 (Classical, Chamber Music)", - wantArtist: "The Brahms Project", - wantYear: 2017, - wantParseOK: true, - }, - { - name: "DSD Mozart symphonies", - title: "[DSD][OF] Concertgebouw Chamber Orchestra - Mozart Symphonies - 2015 (Classical)", - wantArtist: "Concertgebouw Chamber Orchestra", - wantYear: 2015, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if len(tt.wantGenres) > 0 && len(r.Genres) == 0 { - if r.Genres[0] != "Classical" { - t.Errorf("Genres[0] = %q, want Classical", r.Genres[0]) - } - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/discography.go b/internal/tracker/rutracker/parser/discography.go deleted file mode 100644 index d5830ac..0000000 --- a/internal/tracker/rutracker/parser/discography.go +++ /dev/null @@ -1,54 +0,0 @@ -package parser - -import ( - "strings" - - "homelab.lan/music-agregator/internal/release" -) - -type DiscographyParser struct { - BaseParser -} - -func NewDiscographyParser() *DiscographyParser { - return &DiscographyParser{} -} - -func (p *DiscographyParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - - if collectionPattern.MatchString(title) { - r.Type = release.TypeCollection - } else { - r.Type = release.TypeDiscography - } - - r.Artist = p.extractDiscographyArtist(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} - -func (p *DiscographyParser) extractDiscographyArtist(title string) string { - if match := discographyTitlePattern.FindStringSubmatch(title); len(match) >= 2 { - return strings.TrimSpace(match[1]) - } - if match := collectionTitlePattern.FindStringSubmatch(title); len(match) >= 2 { - return strings.TrimSpace(match[1]) - } - artist, _ := p.ExtractArtistAlbum(title) - return artist -} diff --git a/internal/tracker/rutracker/parser/discography_test.go b/internal/tracker/rutracker/parser/discography_test.go deleted file mode 100644 index 3c68c25..0000000 --- a/internal/tracker/rutracker/parser/discography_test.go +++ /dev/null @@ -1,152 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestDiscographyParser(t *testing.T) { - p := NewDiscographyParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantYearEnd int - wantReleaseCount int - wantType release.Type - wantFormat release.AudioFormat - wantParseOK bool - }{ - { - name: "Russian discography with ALAC", - title: "(Metalcore, progressive metalcore, alternative metal, mathcore) [CD`12] Architects - Дискография / Discography - 2006-2025, ALAC (tracks+.cue), lossless", - wantArtist: "Architects", - wantYear: 2006, - wantYearEnd: 2025, - wantType: release.TypeDiscography, - wantFormat: release.FormatALAC, - wantParseOK: true, - }, - { - name: "discography with CD count", - title: "(Rock / Hard Rock / Power-Pop) [CD] Cheap Trick - Дискография - 1977-2021 (78 CD), FLAC (image+.cue), lossless", - wantArtist: "Cheap Trick", - wantYear: 1977, - wantYearEnd: 2021, - wantReleaseCount: 78, - wantType: release.TypeDiscography, - wantParseOK: true, - }, - { - name: "mixed CD and WEB", - title: "Pompeya - Дискография | Discography (3 CD, 6 WEB) - 2011-2015, FLAC (tracks+.cue, tracks/web), lossless", - wantArtist: "Pompeya", - wantYear: 2011, - wantYearEnd: 2015, - wantType: release.TypeDiscography, - wantParseOK: true, - }, - { - name: "large discography with releases count", - title: "(Rock) Александр Башлачёв - Дискография (1994-2025) (35 выпусков, 47 CD / 2 Digital Release), FLAC (image+.cue), lossless", - wantArtist: "Александр Башлачёв", - wantYear: 1994, - wantYearEnd: 2025, - wantType: release.TypeDiscography, - wantParseOK: true, - }, - { - name: "very large discography", - title: "(Rock) Аквариум и Борис Гребенщиков (БГ) - Дискография - 1973–2023 (222 издания, 245 CD), FLAC (image+.cue), lossless", - wantArtist: "Аквариум и Борис Гребенщиков (БГ)", - wantYear: 1973, - wantYearEnd: 2023, - wantType: release.TypeDiscography, - wantParseOK: true, - }, - { - name: "metal discography", - title: "(Heavy Metal) [CD] Saxon - Дискография (58 CD) - 1979-2024, FLAC (image+.cue), lossless", - wantArtist: "Saxon", - wantYear: 1979, - wantYearEnd: 2024, - wantReleaseCount: 58, - wantType: release.TypeDiscography, - wantParseOK: true, - }, - { - name: "detailed Queen discography", - title: "(Progressive Hard Rock Fusion) [CD] Queen – The Discography / Дискография (15 Studio, 11 Live, 13 Compilation, 63 Singles, 2 Collaboration, 7 Box Set, 243 issues, 336 CD) - 1973-2015, FLAC (image+.cue), lossless", - wantArtist: "Queen", - wantYear: 1973, - wantYearEnd: 2015, - wantType: release.TypeDiscography, - wantParseOK: true, - }, - { - name: "English discography", - title: "(Rock, Pop) [CD] U2 - Discography (1980-2017), FLAC (tracks+.cue), lossless", - wantArtist: "U2", - wantYear: 1980, - wantYearEnd: 2017, - wantType: release.TypeDiscography, - wantParseOK: true, - }, - { - name: "death metal discography", - title: "(Technical Brutal Death Metal) [CD] Nile - Discography (1994 - 2024) 13 CD, FLAC (image+.cue), lossless", - wantArtist: "Nile", - wantYear: 1994, - wantYearEnd: 2024, - wantReleaseCount: 13, - wantType: release.TypeDiscography, - wantParseOK: true, - }, - { - name: "collection keyword", - title: "(Pop) Madonna - Коллекция / Collection - 65 релизов (2 Albums, 22 Singles, 13 Megamixes, 8 Live, 17 Collections, 3 Bonus) (1982-2012), MP3, 128-320, VBR kbps", - wantArtist: "Madonna", - wantYear: 1982, - wantYearEnd: 2012, - wantType: release.TypeCollection, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantYearEnd != 0 && r.YearEnd != tt.wantYearEnd { - t.Errorf("YearEnd = %d, want %d", r.YearEnd, tt.wantYearEnd) - } - - if tt.wantReleaseCount != 0 && r.ReleaseCount != tt.wantReleaseCount { - t.Errorf("ReleaseCount = %d, want %d", r.ReleaseCount, tt.wantReleaseCount) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/electronic.go b/internal/tracker/rutracker/parser/electronic.go deleted file mode 100644 index f5025b8..0000000 --- a/internal/tracker/rutracker/parser/electronic.go +++ /dev/null @@ -1,38 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type ElectronicParser struct { - BaseParser -} - -func NewElectronicParser() *ElectronicParser { - return &ElectronicParser{} -} - -func (p *ElectronicParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - if len(r.Genres) == 0 { - r.Genres = []string{"Electronic"} - } - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/electronic_test.go b/internal/tracker/rutracker/parser/electronic_test.go deleted file mode 100644 index 7c99fa9..0000000 --- a/internal/tracker/rutracker/parser/electronic_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestElectronicParser(t *testing.T) { - p := NewElectronicParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantBitrate string - wantParseOK bool - }{ - { - name: "Progressive house VA", - title: "(Progressive House) [WEB] VA - Augmented 018 / FGA (Mango Alley [ALLEYAUG018]) - 2026, FLAC (tracks), lossless", - wantArtist: "VA", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Electro synth-pop tech house", - title: "(Electro, Synth-Pop, Tech House) [CD] VA - Kitsune Maison Compilation 6 - 2008, FLAC (tracks+.cue), lossless", - wantArtist: "VA", - wantYear: 2008, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Deep house multi-CD", - title: "(Deep House, House, Tech House, Minimal Techno) [2 CD] VA - Freza & Nitrous - Air Trip - 2012, FLAC (tracks+.cue), lossless", - wantArtist: "VA", - wantYear: 2012, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "House fresh majestic", - title: "(House) [2 CD] VA - Fresh & Majestic - defile spb [2005] - 2005, FLAC (image+.cue), lossless", - wantArtist: "VA", - wantYear: 2005, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Trance breaks house", - title: "(Trance, Breaks, House) [2 CD] VA - Fantazia - Aural Pleasure - 2000, FLAC (tracks+.cue), lossless", - wantArtist: "VA", - wantYear: 2000, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "House klubnyi", - title: "(House) [CD] VA - E Burg KLUBНЫЙ by Smart #5 - 2006, FLAC (image+.cue), lossless", - wantArtist: "VA", - wantYear: 2006, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "House progressive artist release", - title: "(House, Progressive house) [WEB] Thomas Newson - Summer Vibes (Armada Music[ARMAS1092A]) - 2015, FLAC (tracks), lossless", - wantArtist: "Thomas Newson", - wantYear: 2015, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Progressive trance dream", - title: "(Progressive Trance, Euro House, Trance, Dream) [CD] VA - Dream Power 7 - 1997, FLAC (image+.cue), lossless", - wantArtist: "VA", - wantYear: 1997, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Progressive house hard house", - title: "(Progressive House, Hard House) [CD] VA - Future Russian House - 2001, FLAC (tracks+.cue), lossless", - wantArtist: "VA", - wantYear: 2001, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Progressive house trance 2002", - title: "(Progressive House, Trance) [CD] VA - Future Russian House 2002 - 2002, FLAC (tracks+.cue), lossless", - wantArtist: "VA", - wantYear: 2002, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/folk.go b/internal/tracker/rutracker/parser/folk.go deleted file mode 100644 index 99de775..0000000 --- a/internal/tracker/rutracker/parser/folk.go +++ /dev/null @@ -1,38 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type FolkParser struct { - BaseParser -} - -func NewFolkParser() *FolkParser { - return &FolkParser{} -} - -func (p *FolkParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - if len(r.Genres) == 0 { - r.Genres = []string{"Folk"} - } - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/folk_test.go b/internal/tracker/rutracker/parser/folk_test.go deleted file mode 100644 index 52ea1db..0000000 --- a/internal/tracker/rutracker/parser/folk_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestFolkParser(t *testing.T) { - p := NewFolkParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantBitrate string - wantParseOK bool - }{ - { - name: "Folk italo VA compilation", - title: "(Folk, Italo Folk, Italian Folk) [WEB] VA - La musica della mafia - Best of (Uomini d'onore - Men of Honor) - 2011, FLAC (tracks), lossless", - wantArtist: "VA", - wantYear: 2011, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Russian folk VA", - title: "[RUS](Folk) [CD] VA - Пинежская песня. Том I-III, V, VI - 2011-2016, FLAC (image+.cue), lossless", - wantArtist: "VA", - wantYear: 2011, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Folk world country best of", - title: "(Folk, World, & Country) [CD] Suzy Bogguss - Greatest Hits - 1994, FLAC (tracks+.cue), lossless", - wantArtist: "Suzy Bogguss", - wantYear: 1994, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Folk christmas multi-CD", - title: "(Folk) [CD] The Allisons - Sing Christmas (2 CD) - 1995, FLAC (image+.cue), lossless", - wantArtist: "The Allisons", - wantYear: 1995, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Folk world country album", - title: "(Folk, World, & Country) [CD] Maura O'Connell - Helpless Heart - 1989, FLAC (tracks+.cue), lossless", - wantArtist: "Maura O'Connell", - wantYear: 1989, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Country blues folk collection", - title: "(Country, Blues, Folk, Americana, Guitar, Vocal) [WEB] Jack Barksdale - Collection of 2 Albums, 2 EP and 8 singles / Коллекция из 12 релизов - 2017-2023, FLAC (tracks), lossless", - wantArtist: "Jack Barksdale", - wantYear: 2017, - wantFormat: release.FormatFLAC, - wantType: release.TypeCollection, - wantParseOK: true, - }, - { - name: "Russian dark folk neofolk collection", - title: "[RUS] (Folk, Dark Folk, Neofolk) [CD] Помни Имя Своё - Коллекция (4 релиза, 6 CD) - 2016-2023, FLAC (image+.cue), lossless", - wantArtist: "Помни Имя Своё", - wantYear: 2016, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Folk country rock collection", - title: "(Folk, Country rock) [CD] Emmylou Harris - коллекция 1975-2008 (23 альбома), FLAC (image+.cue, tracks+.cue), lossless", - wantArtist: "Emmylou Harris", - wantYear: 1975, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Ukrainian folk electronic collection", - title: "[UKR] (Folk, Electronic, Dance, Pop) [WEB] Go A - Collection - 2016-2026, FLAC (tracks), 17 CD (1 Album, 16 Singles), lossless", - wantArtist: "Go A", - wantYear: 2016, - wantFormat: release.FormatFLAC, - wantType: release.TypeCollection, - wantParseOK: true, - }, - { - name: "Alternative country folk rock", - title: "(Alternative Country, Folk Rock) [CD] Kathleen Edwards - Asking for Flowers - 2008, FLAC (tracks+.cue), lossless", - wantArtist: "Kathleen Edwards", - wantYear: 2008, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/general.go b/internal/tracker/rutracker/parser/general.go deleted file mode 100644 index 9006553..0000000 --- a/internal/tracker/rutracker/parser/general.go +++ /dev/null @@ -1,35 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type GeneralParser struct { - BaseParser -} - -func NewGeneralParser() *GeneralParser { - return &GeneralParser{} -} - -func (p *GeneralParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/general_test.go b/internal/tracker/rutracker/parser/general_test.go deleted file mode 100644 index e57b87d..0000000 --- a/internal/tracker/rutracker/parser/general_test.go +++ /dev/null @@ -1,166 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestGeneralParser(t *testing.T) { - p := NewGeneralParser() - - tests := []struct { - name string - title string - wantArtist string - wantAlbum string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantGenres []string - wantSource release.Source - wantRipType string - wantBitrate string - wantParseOK bool - }{ - { - name: "standard CD rip with genre", - title: "(Rock) [CD] Thin Lizzy - Acoustic Sessions - 2024 (Decca Records EU 2025), FLAC (image+.cue), lossless", - wantArtist: "Thin Lizzy", - wantAlbum: "Acoustic Sessions", - wantYear: 2024, - wantFormat: release.FormatFLAC, - wantType: release.TypeAlbum, - wantGenres: []string{"Rock"}, - wantSource: release.SourceCD, - wantRipType: "image+.cue", - wantBitrate: "lossless", - wantParseOK: true, - }, - { - name: "multi-genre CD rip", - title: "(Hard Rock, Glam Rock, Progressive Rock, Art Rock, Heavy Metal) [CD] Queen – Queen I (2 CD) – 2024 , FLAC (image+.cue), lossless", - wantArtist: "Queen", - wantYear: 2024, - wantFormat: release.FormatFLAC, - wantType: release.TypeAlbum, - wantGenres: []string{"Hard Rock", "Glam Rock", "Progressive Rock", "Art Rock", "Heavy Metal"}, - wantSource: release.SourceCD, - wantParseOK: true, - }, - { - name: "WEB release with tracks", - title: "(Progressive Rock) [WEB] Opeth - In Cauda Venenum (Extended Edition) - 2019/2022, FLAC (tracks), lossless", - wantArtist: "Opeth", - wantYear: 2019, - wantFormat: release.FormatFLAC, - wantSource: release.SourceWEB, - wantRipType: "tracks", - wantParseOK: true, - }, - { - name: "Japan release", - title: "(Pop-Rock Soft-Rock) [CD] Sting - The Soul Cages (Expanded Edition) - 2025 [Japan], FLAC (image+.cue), lossless", - wantArtist: "Sting", - wantYear: 2025, - wantFormat: release.FormatFLAC, - wantSource: release.SourceCD, - wantParseOK: true, - }, - { - name: "live album", - title: "(Rock) [CD] Bryan Adams - Live at the Royal Albert Hall - 2024, FLAC (image+.cue), lossless", - wantArtist: "Bryan Adams", - wantType: release.TypeLive, - wantYear: 2024, - wantParseOK: true, - }, - { - name: "soundtrack", - title: "(Pop) [CD] Celine Dion - I AM - Celine Dion (Original Motion Picture Soundtrack) - 2024 [Japan], FLAC (image+.cue), lossless", - wantArtist: "Celine Dion", - wantType: release.TypeSoundtrack, - wantYear: 2024, - wantParseOK: true, - }, - { - name: "deluxe box set", - title: "(Rock) [CD] Bryan Adams - Roll With The Punches (Deluxe Box Set) - 2025, FLAC (image+.cue), lossless", - wantArtist: "Bryan Adams", - wantYear: 2025, - wantParseOK: true, - }, - { - name: "CDS single", - title: "(Heavy Metal) [CDS] Bruce Dickinson - Resurrection Men - 2024, FLAC (image+.cue), lossless", - wantArtist: "Bruce Dickinson", - wantYear: 2024, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "tracks+cue format", - title: "(Classic Rock) [CD] The Who - Who Are You (Super Deluxe Edition) - 2025, FLAC (tracks+cue), lossless", - wantArtist: "The Who", - wantYear: 2025, - wantRipType: "tracks+cue", - wantParseOK: true, - }, - { - name: "WEB with special artist name", - title: "(Chamber Pop) [WEB] Florence + the Machine - Ceremonials (Digital Deluxe Edition) - 2011, FLAC (tracks), lossless", - wantArtist: "Florence + the Machine", - wantYear: 2011, - wantSource: release.SourceWEB, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantAlbum != "" && r.Album != tt.wantAlbum { - t.Errorf("Album = %q, want %q", r.Album, tt.wantAlbum) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantSource != release.SourceUnknown && r.Source != tt.wantSource { - t.Errorf("Source = %v, want %v", r.Source, tt.wantSource) - } - - if tt.wantRipType != "" && r.RipType != tt.wantRipType { - t.Errorf("RipType = %q, want %q", r.RipType, tt.wantRipType) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - - if len(tt.wantGenres) > 0 { - if len(r.Genres) != len(tt.wantGenres) { - t.Errorf("Genres count = %d, want %d", len(r.Genres), len(tt.wantGenres)) - } - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/hiphop.go b/internal/tracker/rutracker/parser/hiphop.go deleted file mode 100644 index 2922ca6..0000000 --- a/internal/tracker/rutracker/parser/hiphop.go +++ /dev/null @@ -1,38 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type HipHopParser struct { - BaseParser -} - -func NewHipHopParser() *HipHopParser { - return &HipHopParser{} -} - -func (p *HipHopParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - if len(r.Genres) == 0 { - r.Genres = []string{"Hip-Hop"} - } - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/hiphop_test.go b/internal/tracker/rutracker/parser/hiphop_test.go deleted file mode 100644 index b48d7b4..0000000 --- a/internal/tracker/rutracker/parser/hiphop_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestHipHopParser(t *testing.T) { - p := NewHipHopParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantBitrate string - wantParseOK bool - }{ - { - name: "Rap underground discography", - title: "(Rap | Underground Hip-Hop) Небро | Честер / Chester (Небро) - Дискография, (при уч. НЕ.KURILI) , (39 Релизов), - 2008-2026, MP3, 256-320 kbps", - wantArtist: "Небро | Честер / Chester (Небро)", - wantYear: 2008, - wantFormat: release.FormatMP3, - wantParseOK: true, - }, - { - name: "Rap album MP3", - title: "(Rap) Честер Небро & НЕ.KURILI - Короткометражка - 2026, MP3, 320 kbps", - wantArtist: "Честер Небро & НЕ.KURILI", - wantYear: 2026, - wantFormat: release.FormatMP3, - wantBitrate: "320 kbps", - wantParseOK: true, - }, - { - name: "Hip-hop Kanye West", - title: "(Hip-Hop, Rap, Electronic) [CD][LDR] Ye (Kanye West) - Bully - 2026, FLAC (image+.cue), lossless", - wantArtist: "Ye (Kanye West)", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Rap Russian album", - title: "(Rap) MC Кальмар & Алкоголь После Спорта - Антибиотик - 2025, MP3, 320 kbps", - wantArtist: "MC Кальмар & Алкоголь После Спорта", - wantYear: 2025, - wantFormat: release.FormatMP3, - wantBitrate: "320 kbps", - wantParseOK: true, - }, - { - name: "Rap hip-hop discography complex", - title: "(Rap/Hip-Hop) Killer Chem [Razym Garo] (при участии: AnderМаг, 03406(И.С), 2DT, Бандарад Вирши) - Официальная ДискоТрекография (3 альбома, 8 синглов, Трекография) - 2020-2026, MP3, 192-320 Kbps", - wantArtist: "Killer Chem [Razym Garo] (при участии: AnderМаг, 03406(И.С), 2DT, Бандарад Вирши)", - wantYear: 2020, - wantFormat: release.FormatMP3, - wantParseOK: true, - }, - { - name: "Rap kapa album", - title: "(Rap) Капа - КАПАкалипсис - 2026, MP3, 320 kbps", - wantArtist: "Капа", - wantYear: 2026, - wantFormat: release.FormatMP3, - wantBitrate: "320 kbps", - wantParseOK: true, - }, - { - name: "Rap grot album", - title: "(Rap) Грот - между катастроф - 2026, MP3, 320 kbps", - wantArtist: "Грот", - wantYear: 2026, - wantFormat: release.FormatMP3, - wantBitrate: "320 kbps", - wantParseOK: true, - }, - { - name: "Rap hip-hop collection FLAC", - title: "(Rap, Hip-Hop) [CD] [WEB] † Эйсик (Asick) - Коллекция (13 релизов) - 2005-2023, FLAC (tracks+.cue), lossless", - wantArtist: "† Эйсик (Asick)", - wantYear: 2005, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Hip-hop rap WEB", - title: "(Hip-Hop/Rap) [WEB] Aarne - AA LANGUAGE (Uncensored) - 2022, FLAC (tracks), lossless", - wantArtist: "Aarne", - wantYear: 2022, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Rap fast alberto", - title: "(Rap) Фаст Альберто - Ars Longa Vita Brevis - 2026, MP3, 320 kbps", - wantArtist: "Фаст Альберто", - wantYear: 2026, - wantFormat: release.FormatMP3, - wantBitrate: "320 kbps", - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/hires.go b/internal/tracker/rutracker/parser/hires.go deleted file mode 100644 index c0faf1c..0000000 --- a/internal/tracker/rutracker/parser/hires.go +++ /dev/null @@ -1,45 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type HiResParser struct { - BaseParser -} - -func NewHiResParser() *HiResParser { - return &HiResParser{} -} - -func (p *HiResParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - - if r.Format == release.FormatUnknown { - r.Format = release.FormatFLAC - } - r.Bitrate = "lossless" - - if r.BitDepth == 0 { - if dsdMatch := dsdPattern.FindStringSubmatch(title); len(dsdMatch) >= 3 { - r.BitDepth = 1 - r.Tags = append(r.Tags, dsdMatch[1]+dsdMatch[2]) - } - } - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/hires_test.go b/internal/tracker/rutracker/parser/hires_test.go deleted file mode 100644 index 6fa164b..0000000 --- a/internal/tracker/rutracker/parser/hires_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestHiResParser(t *testing.T) { - p := NewHiResParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantBitDepth int - wantSampleRate int - wantSource release.Source - wantParseOK bool - }{ - { - name: "TR24 OF official release", - title: "[TR24][OF] Matteo Mancuso - Route 96 - 2026 (Progressive Rock, Jazz Fusion, Instrumental)", - wantArtist: "Matteo Mancuso", - wantYear: 2026, - wantParseOK: true, - }, - { - name: "TR24 OF LDR tag", - title: "[TR24][OF][LDR] Sepultura - The Cloud Of Unknowing - 2026 (Groove Thrash Metal)", - wantArtist: "Sepultura", - wantYear: 2026, - wantParseOK: true, - }, - { - name: "24bit 48kHz in title", - title: "[TR24][OF] U2 - Days Of Ash [EP] [24bit-48kHz] - 2026 (Pop Rock, Soft Rock)", - wantArtist: "U2", - wantYear: 2026, - wantBitDepth: 24, - wantSampleRate: 48000, - wantParseOK: true, - }, - { - name: "LP 24/192", - title: "(Blues, R&B) [LP] [24/192] Etta James - At Last! - 1960/2026, FLAC (tracks)", - wantArtist: "Etta James", - wantYear: 1960, - wantBitDepth: 24, - wantSampleRate: 192000, - wantSource: release.SourceVinyl, - wantParseOK: true, - }, - { - name: "DSD128", - title: "(Progressive rock) [LP] [1/5,64 MHz] The Neal Morse Band – L. I. F. T. - 2026, DSD 128 (tracks)", - wantArtist: "The Neal Morse Band", - wantYear: 2026, - wantSource: release.SourceVinyl, - wantParseOK: true, - }, - { - name: "DSD256 with label", - title: "(Jazz, Bop) [LP] [DSD256] Oscar Peterson Trio & Clark Terry - Oscar Peterson Trio + One [Acoustic Sounds Series] - 1964, dsf (tracks)", - wantArtist: "Oscar Peterson Trio & Clark Terry", - wantYear: 1964, - wantParseOK: true, - }, - { - name: "24/96 modal jazz", - title: "(Modal, Jazz) [LP] [24/96] John Coltrane - The Tiberi Tapes: A Preview Of The Mythic Recordings (2026 Record Store Day) - 2026, FLAC (tracks)", - wantArtist: "John Coltrane", - wantYear: 2026, - wantBitDepth: 24, - wantSampleRate: 96000, - wantParseOK: true, - }, - { - name: "2xLP compilation", - title: "(Electronic, Funk / Soul, Disco, House) [2xLP] [24/192] Various - The Many Faces Of Daft Punk - 2020( Compilation), FLAC (tracks)", - wantArtist: "Various", - wantYear: 2020, - wantBitDepth: 24, - wantSampleRate: 192000, - wantParseOK: true, - }, - { - name: "SACD-R", - title: "[SACD-R][OF] Wynton Marsalis - The London Concert - 2000 (Classical)", - wantArtist: "Wynton Marsalis", - wantYear: 2000, - wantParseOK: true, - }, - { - name: "SACD-R DSD", - title: "[SACD-R][DSD][OF]Scott Hamilton, Paolo Birro - Pure Imagination - 2019 (Jazz)", - wantArtist: "Scott Hamilton, Paolo Birro", - wantYear: 2019, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantBitDepth != 0 && r.BitDepth != tt.wantBitDepth { - t.Errorf("BitDepth = %d, want %d", r.BitDepth, tt.wantBitDepth) - } - - if tt.wantSampleRate != 0 && r.SampleRate != tt.wantSampleRate { - t.Errorf("SampleRate = %d, want %d", r.SampleRate, tt.wantSampleRate) - } - - if tt.wantSource != release.SourceUnknown && r.Source != tt.wantSource { - t.Errorf("Source = %v, want %v", r.Source, tt.wantSource) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/jazz.go b/internal/tracker/rutracker/parser/jazz.go deleted file mode 100644 index ff7f0c4..0000000 --- a/internal/tracker/rutracker/parser/jazz.go +++ /dev/null @@ -1,38 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type JazzParser struct { - BaseParser -} - -func NewJazzParser() *JazzParser { - return &JazzParser{} -} - -func (p *JazzParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - if len(r.Genres) == 0 { - r.Genres = []string{"Jazz"} - } - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/jazz_test.go b/internal/tracker/rutracker/parser/jazz_test.go deleted file mode 100644 index 8fa4a63..0000000 --- a/internal/tracker/rutracker/parser/jazz_test.go +++ /dev/null @@ -1,149 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestJazzParser(t *testing.T) { - p := NewJazzParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantSource release.Source - wantType release.Type - wantBitDepth int - wantSampleRate int - wantParseOK bool - }{ - { - name: "Coltrane DSD256 vinyl", - title: "(Jazz, Post Bop, Modal) [LP] [DSD256] The John Coltrane Quartet - The John Coltrane Quartet Plays - 1965, dsf (tracks)", - wantArtist: "The John Coltrane Quartet", - wantYear: 1965, - wantSource: release.SourceVinyl, - wantParseOK: true, - }, - { - name: "Coltrane modal jazz CD", - title: "(Modal Jazz, Hard Bop, Saxophone Jazz) [CD] John Coltrane - Coltrane Jazz - 1961, FLAC (tracks+.cue), lossless", - wantArtist: "John Coltrane", - wantYear: 1961, - wantFormat: release.FormatFLAC, - wantSource: release.SourceCD, - wantParseOK: true, - }, - { - name: "TR24 bebop", - title: "[TR24][OF] Alan Broadbent - Threads of Time - 2025 (Bebop)", - wantArtist: "Alan Broadbent", - wantYear: 2025, - wantParseOK: true, - }, - { - name: "Japanese jazz compilation", - title: "(Fusion, Post-Bop, Modal) [CD] VA - J Jazz Deep Modern Jazz from Japan 1969-1984 - 2018, FLAC (tracks+.cue), lossless", - wantArtist: "VA", - wantYear: 2018, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Fusion WEB release", - title: "(Fusion, Post-Fusion) [WEB] Tucson Modern Jazz Quartet - Eight Myths - 2025, FLAC (tracks), lossless", - wantArtist: "Tucson Modern Jazz Quartet", - wantYear: 2025, - wantSource: release.SourceWEB, - wantParseOK: true, - }, - { - name: "Miles Davis live vinyl 24/96", - title: "(Jazz Rock, Fusion, Psychedelic) [2xLP] [24/96] Miles Davis - Live in Tokyo 1975 - 2015, FLAC (image+.cue)", - wantArtist: "Miles Davis", - wantYear: 2015, - wantType: release.TypeLive, - wantBitDepth: 24, - wantSampleRate: 96000, - wantParseOK: true, - }, - { - name: "Miles Davis Plugged Nickel 24/192", - title: "(Jazz) [LP] [24/192] Miles Davis - Live At The Plugged Nickel December 22 1965 - 2013, FLAC (image+.cue)", - wantArtist: "Miles Davis", - wantYear: 2013, - wantType: release.TypeLive, - wantBitDepth: 24, - wantSampleRate: 192000, - wantParseOK: true, - }, - { - name: "Contemporary jazz CD", - title: "(Post-Bop, Contemporary Jazz) [CD] Billy Hart - Multidirectional - 2025, FLAC (tracks+.cue), lossless", - wantArtist: "Billy Hart", - wantYear: 2025, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Herbie Mann live mono vinyl", - title: "(Jazz, Hard Bop) [LP] [24/192] Herbie Mann - Herbie Mann At The Village Gate - 1962, FLAC (image+.cue)", - wantArtist: "Herbie Mann", - wantYear: 1962, - wantType: release.TypeLive, - wantBitDepth: 24, - wantSampleRate: 192000, - wantParseOK: true, - }, - { - name: "Smooth jazz WEB", - title: "(Smooth Jazz) [WEB] VA - Smooth Jazz Plays Your Favorite Hits - 2006, FLAC (tracks), lossless", - wantArtist: "VA", - wantYear: 2006, - wantSource: release.SourceWEB, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantSource != release.SourceUnknown && r.Source != tt.wantSource { - t.Errorf("Source = %v, want %v", r.Source, tt.wantSource) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantBitDepth != 0 && r.BitDepth != tt.wantBitDepth { - t.Errorf("BitDepth = %d, want %d", r.BitDepth, tt.wantBitDepth) - } - - if tt.wantSampleRate != 0 && r.SampleRate != tt.wantSampleRate { - t.Errorf("SampleRate = %d, want %d", r.SampleRate, tt.wantSampleRate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/label_packs.go b/internal/tracker/rutracker/parser/label_packs.go deleted file mode 100644 index ff7c616..0000000 --- a/internal/tracker/rutracker/parser/label_packs.go +++ /dev/null @@ -1,33 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type LabelPacksParser struct { - BaseParser -} - -func NewLabelPacksParser() *LabelPacksParser { - return &LabelPacksParser{} -} - -func (p *LabelPacksParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - r.Type = release.TypeCollection - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - - if r.Label == "" { - p.AddError(r, "failed to extract label name") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/label_packs_test.go b/internal/tracker/rutracker/parser/label_packs_test.go deleted file mode 100644 index 305ae21..0000000 --- a/internal/tracker/rutracker/parser/label_packs_test.go +++ /dev/null @@ -1,146 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestLabelPacksParser(t *testing.T) { - p := NewLabelPacksParser() - - tests := []struct { - name string - title string - wantLabel string - wantYear int - wantYearEnd int - wantReleaseCount int - wantFormat release.AudioFormat - wantParseOK bool - }{ - { - name: "standard label pack", - title: "(Drum & Bass) [WEB] Label: Metalheadz (370 релизов), 1994-2025, FLAC (tracks), lossless", - wantLabel: "Metalheadz", - wantYear: 1994, - wantYearEnd: 2025, - wantReleaseCount: 370, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "label with part number", - title: "(Trance, House) [WEB] Label: Black Hole Recordings Part 3 (401 Releases) - 2009-2023, FLAC (tracks / images), lossless", - wantLabel: "Black Hole Recordings Part 3", - wantYear: 2009, - wantYearEnd: 2023, - wantReleaseCount: 401, - wantParseOK: true, - }, - { - name: "small label", - title: "(Trance) [WEB, CD] Label: Solaris Recordings (7 Releases) - 2005-2014, FLAC (tracks, tracks+.cue), lossless", - wantLabel: "Solaris Recordings", - wantYear: 2005, - wantYearEnd: 2014, - wantParseOK: true, - }, - { - name: "techno label with brackets", - title: "(Techno, IDM, Experimental) [WEB,CD] Label: Stroboscopic Artefacts (96 Releases) - 2009-2022, FLAC (tracks) (tracks+.cue), lossless", - wantLabel: "Stroboscopic Artefacts", - wantYear: 2009, - wantYearEnd: 2022, - wantReleaseCount: 96, - wantParseOK: true, - }, - { - name: "multi-genre label", - title: "(Techno, Ambient, IDM, Experimental, Drum n Bass) [WEB,CD] Label: Auxiliary (65 Releases) - 2010-2021, FLAC (tracks) (tracks+.cue), lossless", - wantLabel: "Auxiliary", - wantYear: 2010, - wantYearEnd: 2021, - wantReleaseCount: 65, - wantParseOK: true, - }, - { - name: "Russian release count", - title: "(Techno, Minimal, Deep Tech, Melodic House & Techno) [WEB] Label: FCKNG SERIOUS (121 релиз), 2015-2025, FLAC (tracks, image), lossless", - wantLabel: "FCKNG SERIOUS", - wantYear: 2015, - wantYearEnd: 2025, - wantReleaseCount: 121, - wantParseOK: true, - }, - { - name: "progressive house label", - title: "(Progressive House, Trance, Techno) [WEB] Label: Bedrock Records (519 релизов), 1999-2025, (FLAC) lossless (tracks, image)", - wantLabel: "Bedrock Records", - wantYear: 1999, - wantYearEnd: 2025, - wantReleaseCount: 519, - wantParseOK: true, - }, - { - name: "large techno label", - title: "(Techno) [WEB,CD] Label: Planet Rhythm Records (443 Releases) - 1994-2021, FLAC (tracks) (tracks+.cue, image+.cue), lossless", - wantLabel: "Planet Rhythm Records", - wantYear: 1994, - wantYearEnd: 2021, - wantReleaseCount: 443, - wantParseOK: true, - }, - { - name: "label with featured artists", - title: "(Trance, Breaks, House) [WEB] Label: Digital Emotions (47 Releases) (Incl. Fonarev pres. F13, Poshout, Second Sine & etc.) - 2010-2025, FLAC (tracks), lossless", - wantLabel: "Digital Emotions", - wantYear: 2010, - wantYearEnd: 2025, - wantParseOK: true, - }, - { - name: "bondage music", - title: "(Deep House, Minimal) [WEB] Label: Bondage Music (173 релиза), 2006-2025, (FLAC) lossless (tracks, image)", - wantLabel: "Bondage Music", - wantYear: 2006, - wantYearEnd: 2025, - wantReleaseCount: 173, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantLabel != "" && r.Label != tt.wantLabel { - t.Errorf("Label = %q, want %q", r.Label, tt.wantLabel) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantYearEnd != 0 && r.YearEnd != tt.wantYearEnd { - t.Errorf("YearEnd = %d, want %d", r.YearEnd, tt.wantYearEnd) - } - - if tt.wantReleaseCount != 0 && r.ReleaseCount != tt.wantReleaseCount { - t.Errorf("ReleaseCount = %d, want %d", r.ReleaseCount, tt.wantReleaseCount) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if r.Type != release.TypeCollection { - t.Errorf("Type = %v, want Collection", r.Type) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/lossless.go b/internal/tracker/rutracker/parser/lossless.go deleted file mode 100644 index bfb9dae..0000000 --- a/internal/tracker/rutracker/parser/lossless.go +++ /dev/null @@ -1,37 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type LosslessParser struct { - BaseParser -} - -func NewLosslessParser() *LosslessParser { - return &LosslessParser{} -} - -func (p *LosslessParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Format == release.FormatUnknown { - r.Format = release.FormatFLAC - } - r.Bitrate = "lossless" - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/lossless_test.go b/internal/tracker/rutracker/parser/lossless_test.go deleted file mode 100644 index 6964859..0000000 --- a/internal/tracker/rutracker/parser/lossless_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestLosslessParser(t *testing.T) { - p := NewLosslessParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantSource release.Source - wantRipType string - wantParseOK bool - }{ - { - name: "standard CD FLAC image", - title: "(Rock) [CD] Thin Lizzy - Acoustic Sessions - 2024 (Decca Records EU 2025), FLAC (image+.cue), lossless", - wantArtist: "Thin Lizzy", - wantYear: 2024, - wantFormat: release.FormatFLAC, - wantSource: release.SourceCD, - wantRipType: "image+.cue", - wantParseOK: true, - }, - { - name: "WEB release tracks", - title: "(Progressive Rock) [WEB] Opeth - In Cauda Venenum (Extended Edition) - 2019/2022, FLAC (tracks), lossless", - wantArtist: "Opeth", - wantYear: 2019, - wantFormat: release.FormatFLAC, - wantSource: release.SourceWEB, - wantRipType: "tracks", - wantParseOK: true, - }, - { - name: "APE format", - title: "(Jazz) [CD] Miles Davis - Kind of Blue - 1959, APE (image+.cue), lossless", - wantArtist: "Miles Davis", - wantYear: 1959, - wantFormat: release.FormatAPE, - wantSource: release.SourceCD, - wantParseOK: true, - }, - { - name: "tracks+cue format", - title: "(Classic Rock) [CD] The Who - Who Are You (Super Deluxe Edition) - 2025, FLAC (tracks+cue), lossless", - wantArtist: "The Who", - wantYear: 2025, - wantFormat: release.FormatFLAC, - wantRipType: "tracks+cue", - wantParseOK: true, - }, - { - name: "multi-disc set", - title: "(Hard Rock, Glam Rock, Progressive Rock, Art Rock, Heavy Metal) [CD] Queen – Queen I (2 CD) – 2024 , FLAC (image+.cue), lossless", - wantArtist: "Queen", - wantYear: 2024, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Japan release", - title: "(Pop-Rock Soft-Rock) [CD] Sting - The Soul Cages (Expanded Edition) - 2025 [Japan], FLAC (image+.cue), lossless", - wantArtist: "Sting", - wantYear: 2025, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "WavPack format", - title: "(Progressive Rock) [CD] Yes - Close to the Edge - 1972, WV (image+.cue), lossless", - wantArtist: "Yes", - wantYear: 1972, - wantFormat: release.FormatWavPack, - wantParseOK: true, - }, - { - name: "default to FLAC when format not specified", - title: "(Rock) [CD] Pink Floyd - The Wall - 1979 (image+.cue), lossless", - wantArtist: "Pink Floyd", - wantYear: 1979, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "heavy metal WEB", - title: "(Heavy Metal) [WEB] Heaven & Hell - Breaking Out Of Heaven - 2026, FLAC (tracks), lossless", - wantArtist: "Heaven & Hell", - wantYear: 2026, - wantSource: release.SourceWEB, - wantParseOK: true, - }, - { - name: "melodic rock WEB", - title: "(Melodic Rock, Progressive Rock) [WEB] James LaBrie - Beautiful Shade Of Grey - 2022, FLAC (tracks), lossless", - wantArtist: "James LaBrie", - wantYear: 2022, - wantSource: release.SourceWEB, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantSource != release.SourceUnknown && r.Source != tt.wantSource { - t.Errorf("Source = %v, want %v", r.Source, tt.wantSource) - } - - if tt.wantRipType != "" && r.RipType != tt.wantRipType { - t.Errorf("RipType = %q, want %q", r.RipType, tt.wantRipType) - } - - if r.Bitrate != "lossless" { - t.Errorf("Bitrate = %q, want lossless", r.Bitrate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/lossy.go b/internal/tracker/rutracker/parser/lossy.go deleted file mode 100644 index 54f78aa..0000000 --- a/internal/tracker/rutracker/parser/lossy.go +++ /dev/null @@ -1,36 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type LossyParser struct { - BaseParser -} - -func NewLossyParser() *LossyParser { - return &LossyParser{} -} - -func (p *LossyParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.Tags = p.ExtractSpecialTags(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Format == release.FormatUnknown { - r.Format = release.FormatMP3 - } - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/lossy_test.go b/internal/tracker/rutracker/parser/lossy_test.go deleted file mode 100644 index 155f594..0000000 --- a/internal/tracker/rutracker/parser/lossy_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestLossyParser(t *testing.T) { - p := NewLossyParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantBitrate string - wantType release.Type - wantParseOK bool - }{ - { - name: "VBR V0", - title: "(Pop) VA - Pop Classics Top 100 - 2012, MP3, VBR V0", - wantArtist: "VA", - wantYear: 2012, - wantFormat: release.FormatMP3, - wantBitrate: "V0", - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "VBR V0 kbps suffix", - title: "(Pop/Rock) VA - 101 Ultimate 80's (5 CD) - 2011, MP3 (tracks), VBR V0 kbps", - wantArtist: "VA", - wantYear: 2011, - wantFormat: release.FormatMP3, - wantBitrate: "V0", - wantParseOK: true, - }, - { - name: "VBR V1", - title: "(Rock) VA - Greatest Ever! Rock The Definitive Collection (3 CD) - 2006, MP3 (tracks), VBR V1 kbps", - wantArtist: "VA", - wantYear: 2006, - wantBitrate: "V1", - wantParseOK: true, - }, - { - name: "VBR V2", - title: "(Classic Rock) VA - Twist & Shout - 2005, MP3, VBR V2", - wantArtist: "VA", - wantYear: 2005, - wantBitrate: "V2", - wantParseOK: true, - }, - { - name: "VBR range", - title: "(Pop, Rock) VA - The Essential 1980s - 2010, MP3 (tracks), VBR 192-320 kbps", - wantArtist: "VA", - wantYear: 2010, - wantBitrate: "VBR 192-320 kbps", - wantParseOK: true, - }, - { - name: "CBR 320", - title: "(Pop) VA - Bravo Hits, Vol. 128 [2 CD] - 2025, MP3, 320 kbps", - wantArtist: "VA", - wantYear: 2025, - wantBitrate: "320 kbps", - wantParseOK: true, - }, - { - name: "CBR 256", - title: "(Rock'n'Roll) VA - Rock-n-roll The Best Hits - 2005, MP3 (tracks), 256 kbps", - wantArtist: "VA", - wantYear: 2005, - wantFormat: release.FormatMP3, - wantBitrate: "256 kbps", - wantParseOK: true, - }, - { - name: "year range in title", - title: "(Pop) VA - Bravo Hits vol. 31-59 - 2000-2007, MP3, VBR 192-320 kbps", - wantArtist: "VA", - wantYear: 2000, - wantParseOK: true, - }, - { - name: "discography in lossy", - title: "(Alternative Metal / Post-Grunge) Breaking Benjamin - Discography: 23 Releases, 2001-2024, MP3, VBR V0/320 kbps", - wantArtist: "Breaking Benjamin", - wantYear: 2001, - wantType: release.TypeDiscography, - wantParseOK: true, - }, - { - name: "bootleg release", - title: "(Eurodance) VA - Beat Mix Eurodance Vol 1-3 (Bootlegs) - 2009-2011, MP3 (image), VBR V2 / V0", - wantArtist: "VA", - wantYear: 2009, - wantType: release.TypeBootleg, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/metal.go b/internal/tracker/rutracker/parser/metal.go deleted file mode 100644 index 214ce80..0000000 --- a/internal/tracker/rutracker/parser/metal.go +++ /dev/null @@ -1,38 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type MetalParser struct { - BaseParser -} - -func NewMetalParser() *MetalParser { - return &MetalParser{} -} - -func (p *MetalParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - if len(r.Genres) == 0 { - r.Genres = []string{"Metal"} - } - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/metal_test.go b/internal/tracker/rutracker/parser/metal_test.go deleted file mode 100644 index 8fb0338..0000000 --- a/internal/tracker/rutracker/parser/metal_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestMetalParser(t *testing.T) { - p := NewMetalParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantBitrate string - wantParseOK bool - }{ - { - name: "Death metal EP", - title: "(Death Metal) Monolithic Terror - A Time To Kill (EP) - 2026, MP3, 320 kbps", - wantArtist: "Monolithic Terror", - wantYear: 2026, - wantFormat: release.FormatMP3, - wantType: release.TypeEP, - wantBitrate: "320 kbps", - wantParseOK: true, - }, - { - name: "Heavy metal album", - title: "(Heavy Metal) More - Destructor - 2026, MP3, 320 kbps", - wantArtist: "More", - wantYear: 2026, - wantFormat: release.FormatMP3, - wantParseOK: true, - }, - { - name: "Melodic death metal EP", - title: "(Melodic Death Metal) Death Brigade - Rites Of War (EP) - 2026, MP3, 320 kbps", - wantArtist: "Death Brigade", - wantYear: 2026, - wantType: release.TypeEP, - wantParseOK: true, - }, - { - name: "Power metal WEB FLAC", - title: "(Heavy Metal, Power Metal) [WEB] Death Dealer - Reign of Steel - 2026, FLAC (tracks), lossless", - wantArtist: "Death Dealer", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Thrash metal deluxe box", - title: "(Heavy/Power/Thrash Metal) Metal Church - Dead to Rights (Deluxe Box Set Edition) - 2026, MP3, 320 kbps", - wantArtist: "Metal Church", - wantYear: 2026, - wantParseOK: true, - }, - { - name: "Iron Maiden discography", - title: "(Heavy Metal, Hard Rock) Iron Maiden - Discography (146 CD + 4 WEB) - 1979-2021, AAC (tracks), VBR 320 kbps", - wantArtist: "Iron Maiden", - wantYear: 1979, - wantType: release.TypeDiscography, - wantFormat: release.FormatAAC, - wantParseOK: true, - }, - { - name: "Black metal restored", - title: "[RM] [restored] [declipped] [16/44] (Black Metal) Mayhem - 15 releases - 1987-2026, FLAC (tracks+.cue), lossless", - wantArtist: "Mayhem", - wantYear: 1987, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Black metal vinyl 24/96", - title: "(Black Metal) [LP] [24/96] Hellhammer - Apocalyptic Raids - 1984, FLAC (tracks)", - wantArtist: "Hellhammer", - wantYear: 1984, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Russian thrash vinyl rip", - title: "(Thrash Metal) КОРРОЗИЯ МЕТАЛЛА - Каннибал (VINYL RIP) - 1990, FLAC (image+.cue), lossless", - wantArtist: "КОРРОЗИЯ МЕТАЛЛА", - wantYear: 1990, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Progressive metal live", - title: "(Progressive Metal) Leprous - An Evening of Atonement (Live in Tilburg 2025) [2 CD] - 2025, MP3, 320 kbps", - wantArtist: "Leprous", - wantYear: 2025, - wantType: release.TypeLive, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/misc_music.go b/internal/tracker/rutracker/parser/misc_music.go deleted file mode 100644 index d6439a1..0000000 --- a/internal/tracker/rutracker/parser/misc_music.go +++ /dev/null @@ -1,37 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type MiscMusicParser struct { - BaseParser -} - -func NewMiscMusicParser() *MiscMusicParser { - return &MiscMusicParser{} -} - -func (p *MiscMusicParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - if len(r.Genres) == 0 { - } - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/misc_music_test.go b/internal/tracker/rutracker/parser/misc_music_test.go deleted file mode 100644 index 6d257d5..0000000 --- a/internal/tracker/rutracker/parser/misc_music_test.go +++ /dev/null @@ -1,144 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestMiscMusicParser(t *testing.T) { - p := NewMiscMusicParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantBitrate string - wantParseOK bool - }{ - { - name: "Spiritual chants FLAC", - title: "(Духовные песнопения) [CD] Хор Сретенского Монастыря - Рождественские песнопения - 2005, FLAC (image+.cue), lossless", - wantArtist: "Хор Сретенского Монастыря", - wantYear: 2005, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Children songs VA", - title: "(Детские песни) [CD] VA - Любимые песни из мультфильмов - 2010, FLAC (tracks), lossless", - wantArtist: "VA", - wantYear: 2010, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Ballroom latin VA", - title: "(Ballroom, Latin) [CD] VA - Dancelife: The Best Of Latin Music - 2008, FLAC (tracks+.cue), lossless", - wantArtist: "VA", - wantYear: 2008, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Nature sounds MP3", - title: "(Nature Sounds) VA - Sounds Of Nature: Rainforest - 2005, MP3, 320 kbps", - wantArtist: "VA", - wantYear: 2005, - wantFormat: release.FormatMP3, - wantBitrate: "320 kbps", - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Spiritual gregorian WEB", - title: "(Spiritual) [WEB] Gregorian - Masters of Chant - 2006, FLAC (tracks), lossless", - wantArtist: "Gregorian", - wantYear: 2006, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Mixed styles VA compilation", - title: "(Mixed Styles) VA - Various Artists Compilation 2024 - 2024, MP3, 320 kbps", - wantArtist: "VA", - wantYear: 2024, - wantFormat: release.FormatMP3, - wantBitrate: "320 kbps", - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Children lullabies WEB", - title: "(Детские песни) [WEB] VA - Колыбельные для малышей - 2020, FLAC (tracks), lossless", - wantArtist: "VA", - wantYear: 2020, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Orthodox chants CD", - title: "(Духовная музыка) [CD] VA - Православные песнопения - 2012, FLAC (image+.cue), lossless", - wantArtist: "VA", - wantYear: 2012, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Ballroom cha cha cha", - title: "(Ballroom) [CD] VA - Strictly Ballroom Dancing - Cha Cha Cha - 2006, FLAC (tracks+.cue), lossless", - wantArtist: "VA", - wantYear: 2006, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Nature sounds relaxation", - title: "(Nature Sounds, Relaxation) VA - Ocean Waves: Calm & Relax - 2018, MP3, 256 kbps", - wantArtist: "VA", - wantYear: 2018, - wantFormat: release.FormatMP3, - wantBitrate: "256 kbps", - wantType: release.TypeCompilation, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/patterns.go b/internal/tracker/rutracker/parser/patterns.go deleted file mode 100644 index c84b5cb..0000000 --- a/internal/tracker/rutracker/parser/patterns.go +++ /dev/null @@ -1,109 +0,0 @@ -package parser - -import "regexp" - -var ( - // Genre at start: (Rock), (Electronic, Ambient), (Jazz / Blues) - genrePattern = regexp.MustCompile(`^\s*\(([^)]+)\)\s*`) - - // Label pack: Label: Name or Label - Name - labelPattern = regexp.MustCompile(`(?i)Label\s*[:\-]\s*([^\[(]+?)(?:\s*[\[(]|\s*$)`) - - // Year: single or range - yearPattern = regexp.MustCompile(`\b((?:19|20)\d{2})\b`) - yearRangePattern = regexp.MustCompile(`\b((?:19|20)\d{2})\s*[-–]\s*((?:19|20)\d{2})\b`) - - // Reissue year format: 1960/2026 (original/reissue) → capture first - reissueYearPattern = regexp.MustCompile(`\b((?:19|20)\d{2})/((?:19|20)\d{2})\b`) - - // Release year after dash: " - YEAR" or " - YEAR," or " - YEAR (" - releaseYearPattern = regexp.MustCompile(`\s[-–]\s*((?:19|20)\d{2})(?:[,\s(]|$)`) - - // Release count: (15 CD), (30 albums), 10 releases, (50 релизов), 13 CD - releaseCountPattern = regexp.MustCompile(`(?i)(?:\()?(\d+)\s*(?:CD|albums?|releases?|релиз(?:а|ов)?|альбом(?:а|ов)?)(?:\))?`) - - // Audio formats - formatPattern = regexp.MustCompile(`(?i)\b(FLAC|APE|MP3|AAC|OGG|WV|WavPack|ALAC|WAV|DSD\d*|DST\d*)\b`) - - // Bitrate: 320 kbps, V0, VBR 192-320 kbps, lossless - bitratePattern = regexp.MustCompile(`(?i)(?:(\d{2,3})\s*kbps|V([012])|VBR\s*(?:~?(\d+)|(\d+)-(\d+))\s*kbps|lossless)`) - - // Rip type: image+.cue, tracks+.cue, tracks - ripTypePattern = regexp.MustCompile(`(?i)(image\+\.?cue|tracks?\+\.?cue|tracks?)`) - - // Hi-Res bit depth / sample rate: [24/96], [24/192], [24bit-48kHz] - hiResPattern = regexp.MustCompile(`\[(\d+)(?:/|bit[/-])(\d+(?:\.\d+)?)\s*(?:kHz)?\]`) - - // DSD formats: DSD64, DSD128, DST64 - dsdPattern = regexp.MustCompile(`(?i)\b(DSD|DST)(64|128|256|512)\b`) - - // Source tags: [CD], [WEB], [LP], [Vinyl], [SACD], [DVDA] - sourceTagPattern = regexp.MustCompile(`(?i)\[(CD|WEB|LP|Vinyl|SACD|DVDA|HDAD|MINI-LP|EP|12"|10"|7")\]`) - - // Vinyl condition: [NM], [EX], [VG+], [VG], [G], [Mint], [SS] - vinylConditionPattern = regexp.MustCompile(`\[(Mint|SS|NM|EX|VG\+?|G|F/?P)\]`) - - // Special tags: [AI], [WEB], [TR24], [OF], [RM], [restored], [declipped] - specialTagPattern = regexp.MustCompile(`\[(AI|WEB|TR24|OF|RM|restored|declipped)\]`) - - // Discography keywords (Russian + English) - discographyPattern = regexp.MustCompile(`(?i)\b([Дд]искографи[яи]|[Dd]iscograph(?:y|ies))\b`) - - // Collection keywords - collectionPattern = regexp.MustCompile(`(?i)\b([Кк]оллекци[яи]|[Cc]ollection|[Cc]omplete\s+(?:[Ss]tudio\s+)?[Rr]ecordings?)\b`) - - // Compilation keywords - compilationPattern = regexp.MustCompile(`(?i)\b([Сс]борник|[Cc]ompilation|[Vv]arious\s*[Aa]rtists?|VA)\b`) - - // Anthology keywords - anthologyPattern = regexp.MustCompile(`(?i)\b([Аа]нтологи[яи]|[Aa]nthology)\b`) - - // Best of / Greatest hits keywords - bestOfPattern = regexp.MustCompile(`(?i)\b([Ии]збранное|[Лл]учшее|[Bb]est\s*[Oo]f|[Gg]reatest\s*[Hh]its)\b`) - - // Live / Concert keywords including venue patterns - livePattern = regexp.MustCompile(`(?i)(\b[Жж]ивой\b|\b[Кк]онцерт\b|\b[Ll]ive\b|\b[Cc]oncert\b|[Ll]ive\s*[Aa]t|[Aa]t\s+[Tt]he\s+\w+)`) - - // Bootleg keywords - bootlegPattern = regexp.MustCompile(`(?i)\b([Бб]утлеги?|[Bb]ootlegs?|[Uu]nofficial)\b`) - - // Soundtrack keywords - soundtrackPattern = regexp.MustCompile(`(?i)\b(OST|[Ss]oundtrack|[Сс]аундтрек|[Ss]core|[Мм]узыка\s*(?:к|из)\s*фильм[ау])\b`) - - // Remaster keywords - remasterPattern = regexp.MustCompile(`(?i)\b([Рр]емастер|[Rr]emaster(?:ed)?|[Пп]ереиздани[ея]|[Rr]e-?issue)\b`) - - // EP keywords - epPattern = regexp.MustCompile(`(?i)\b(EP|[Мм]ини[-\s]?[Аа]льбом|[Ee]xtended\s*[Pp]lay)\b`) - - // Single keywords - singlePattern = regexp.MustCompile(`(?i)\b([Сс]ингл|[Ss]ingle)\b`) - - // Standard title format: Artist - Album - Year or (Genre) Artist - Album - Year - // Captures: artist, album, year - standardTitlePattern = regexp.MustCompile(`^(?:\([^)]+\)\s*)?(?:\[[^\]]+\]\s*)*([^-–]+?)\s*[-–]\s*(.+?)\s*[-–]\s*((?:19|20)\d{2})`) - - // Alternative: Artist - Album (Year) - altTitlePattern = regexp.MustCompile(`^(?:\([^)]+\)\s*)?(?:\[[^\]]+\]\s*)*([^-–]+?)\s*[-–]\s*(.+?)\s*\(((?:19|20)\d{2})\)`) - - // Discography title: Artist - Дискография (15 CD) [1990-2020, ...] - discographyTitlePattern = regexp.MustCompile(`^(?:\([^)]+\)\s*)?(?:\[[^\]]+\]\s*)*([^-–]+?)\s*[-–]\s*(?:[Дд]искографи[яи]|[Dd]iscograph(?:y|ies))`) - - // Collection title: Artist - Коллекция (50 CD) [1980-2019, ...] - collectionTitlePattern = regexp.MustCompile(`^(?:\([^)]+\)\s*)?(?:\[[^\]]+\]\s*)*([^-–]+?)\s*[-–]\s*(?:[Кк]оллекци[яи]|[Cc]ollection)`) - - // Label pack title: (Genre) Label: Label Name (releases) - labelPackTitlePattern = regexp.MustCompile(`^(?:\([^)]+\)\s*)?(?i)Label:\s*([^(]+)`) - - // Catalog number in brackets: [CAT001], [LABEL-001] - catalogNumPattern = regexp.MustCompile(`\[([A-Z]{2,}[-\s]?\d+[A-Z]*)\]`) - - // Tags in brackets at start to strip: [RM], [restored], etc. (before or after genre) - leadingTagsPattern = regexp.MustCompile(`^(\s*\[[^\]]+\]\s*)+`) - - // Tags before genre pattern: [RM] [restored] (Genre) - tagsBeforeGenrePattern = regexp.MustCompile(`^(\s*\[[^\]]+\]\s*)+\([^)]+\)\s*`) - - // Clean trailing technical info: , FLAC (image+.cue) - trailingTechPattern = regexp.MustCompile(`,?\s*(?:FLAC|APE|MP3|AAC|OGG|WV|WavPack|ALAC|WAV).*$`) -) diff --git a/internal/tracker/rutracker/parser/pop.go b/internal/tracker/rutracker/parser/pop.go deleted file mode 100644 index fb70a1b..0000000 --- a/internal/tracker/rutracker/parser/pop.go +++ /dev/null @@ -1,38 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type PopParser struct { - BaseParser -} - -func NewPopParser() *PopParser { - return &PopParser{} -} - -func (p *PopParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - if len(r.Genres) == 0 { - r.Genres = []string{"Pop"} - } - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/pop_test.go b/internal/tracker/rutracker/parser/pop_test.go deleted file mode 100644 index d0557ff..0000000 --- a/internal/tracker/rutracker/parser/pop_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestPopParser(t *testing.T) { - p := NewPopParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantBitrate string - wantParseOK bool - }{ - { - name: "Pop CD album", - title: "(Pop) [CD] Nessa Barrett - AFTERCARE DELUXE - 2025, FLAC (tracks+.cue), lossless", - wantArtist: "Nessa Barrett", - wantYear: 2025, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Pop alternative deluxe", - title: "(Pop) (Alternative) [CD] Melanie Martinez - Cry Baby (Deluxe Edition) - 2015, FLAC (tracks+.cue), lossless", - wantArtist: "Melanie Martinez", - wantYear: 2015, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Pop album", - title: "(Pop) [CD] Ava Max - Diamonds & Dancefloors - 2023, FLAC (tracks+.cue), lossless", - wantArtist: "Ava Max", - wantYear: 2023, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Pop VA compilation", - title: "(Pop) [WEB] VA - Музыка Победы - 2025, FLAC (tracks), lossless", - wantArtist: "VA", - wantYear: 2025, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Pop limited edition", - title: "(Pop) [CD] Стрелки - Gold [Limited Edition] Maschina Records - 2026, FLAC (image+.cue), lossless", - wantArtist: "Стрелки", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Pop Europop", - title: "(Pop, Europop) [CD] Pupo - Insieme (2025), FLAC (image+.cue), lossless", - wantArtist: "Pupo", - wantYear: 2025, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Pop VA multi-CD", - title: "(Pop) [CD] VA - The Best Of 1980-1990 Vol. II [3 CD] - 1990, FLAC (tracks+.cue), lossless", - wantArtist: "VA", - wantYear: 1990, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Pop dont click play", - title: "(Pop) [CD] Ava Max - Don't Click Play - 2025, FLAC (tracks+.cue), lossless", - wantArtist: "Ava Max", - wantYear: 2025, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Pop no good", - title: "(Pop) [CD] Ivy Levan - No Good - 2015, FLAC (tracks+.cue), lossless", - wantArtist: "Ivy Levan", - wantYear: 2015, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Pop vocal soul", - title: "(Pop, Vocal, Soul, R&B) [WEB] Hush Dusty - Love is a Battlefield Tonight - 2026, FLAC (tracks), lossless", - wantArtist: "Hush Dusty", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/radioshow.go b/internal/tracker/rutracker/parser/radioshow.go deleted file mode 100644 index b5d25ab..0000000 --- a/internal/tracker/rutracker/parser/radioshow.go +++ /dev/null @@ -1,38 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type RadioshowParser struct { - BaseParser -} - -func NewRadioshowParser() *RadioshowParser { - return &RadioshowParser{} -} - -func (p *RadioshowParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - if len(r.Genres) == 0 { - r.Genres = []string{"Electronic"} - } - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/radioshow_test.go b/internal/tracker/rutracker/parser/radioshow_test.go deleted file mode 100644 index e82494c..0000000 --- a/internal/tracker/rutracker/parser/radioshow_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestRadioshowParser(t *testing.T) { - p := NewRadioshowParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantBitrate string - wantParseOK bool - }{ - { - name: "BBC Essential Mix AAC", - title: "(House, Progressive House, Tech House, Dance, Electro, DnB) BBC Radio One - Essential Mix 2026, AAC (tracks), 320 kbps", - wantArtist: "BBC Radio One", - wantYear: 2026, - wantFormat: release.FormatAAC, - wantBitrate: "320 kbps", - wantParseOK: true, - }, - { - name: "Russian mega mix MP3", - title: "(Club House, Progressive House, Russian Pop) Alex Kerdivar - Russian Mega Mix 21 (26.04.2026), MP3, 320 kbps", - wantArtist: "Alex Kerdivar", - wantYear: 2026, - wantFormat: release.FormatMP3, - wantBitrate: "320 kbps", - wantParseOK: true, - }, - { - name: "Drum and bass fussy listener", - title: "(Intelligent Drum & Bass) LTJ Bukem - Fussy Listener Mix Vol 3 - 11.02.2026, MP3, 192 kbps", - wantArtist: "LTJ Bukem", - wantYear: 2026, - wantFormat: release.FormatMP3, - wantBitrate: "192 kbps", - wantParseOK: true, - }, - { - name: "Neurofunk BBC Radio", - title: "(Neurofunk) Enta - Production Showcase Mix (BBC Radio 1) - 17.11.2024, MP3, 320 kbps", - wantArtist: "Enta", - wantYear: 2024, - wantFormat: release.FormatMP3, - wantBitrate: "320 kbps", - wantParseOK: true, - }, - { - name: "Dark techstep methlab radio", - title: "(Drum & Bass, Dark Techstep) Allied - MethLab Radio Guest Mix [MLR040] - 05.11.2015, MP3, 320 kbps", - wantArtist: "Allied", - wantYear: 2015, - wantFormat: release.FormatMP3, - wantBitrate: "320 kbps", - wantParseOK: true, - }, - { - name: "Goldie 6 mix VBR", - title: "(Drum & Bass) Goldie - The 6 Mix (BBC Radio 6) - 06-06-2025, MP3, V0", - wantArtist: "Goldie", - wantYear: 2025, - wantFormat: release.FormatMP3, - wantBitrate: "V0", - wantParseOK: true, - }, - { - name: "Daphni Essential Mix", - title: "(House, Tech House) Daphni - BBC Radio 1s Essential Mix - 17-01-2026, MP3, V0", - wantArtist: "Daphni", - wantYear: 2026, - wantFormat: release.FormatMP3, - wantBitrate: "V0", - wantParseOK: true, - }, - { - name: "Andy C 6 mix", - title: "(Drum & Bass) Andy C - The 6 Mix (BBC Radio 6) 16-01-2026, MP3, V0", - wantArtist: "Andy C", - wantYear: 2026, - wantFormat: release.FormatMP3, - wantBitrate: "V0", - wantParseOK: true, - }, - { - name: "Club house Russian rap mix", - title: "(Club House, Russian Rap, Rap, Hip-Hop) Alex Kerdivar - Special Mega Mix 14 (17.01.2026), MP3, 320 kbps", - wantArtist: "Alex Kerdivar", - wantYear: 2026, - wantFormat: release.FormatMP3, - wantBitrate: "320 kbps", - wantParseOK: true, - }, - { - name: "Jungle phonica mix series", - title: "(Drum & Bass, Jungle) Tim Reaper - Phonica Mix Series 128 (DJ Mix) - 2025, MP3, 320 kbps", - wantArtist: "Tim Reaper", - wantYear: 2025, - wantFormat: release.FormatMP3, - wantBitrate: "320 kbps", - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/reggae.go b/internal/tracker/rutracker/parser/reggae.go deleted file mode 100644 index b673709..0000000 --- a/internal/tracker/rutracker/parser/reggae.go +++ /dev/null @@ -1,38 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type ReggaeParser struct { - BaseParser -} - -func NewReggaeParser() *ReggaeParser { - return &ReggaeParser{} -} - -func (p *ReggaeParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - if len(r.Genres) == 0 { - r.Genres = []string{"Reggae"} - } - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/reggae_test.go b/internal/tracker/rutracker/parser/reggae_test.go deleted file mode 100644 index adc7218..0000000 --- a/internal/tracker/rutracker/parser/reggae_test.go +++ /dev/null @@ -1,134 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestReggaeParser(t *testing.T) { - p := NewReggaeParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantBitrate string - wantParseOK bool - }{ - { - name: "Reggae funk soul album", - title: "(Reggae, Funk / Soul) [CD] Diana King - Think Like A Girl (CD Album, Enhanced) - 1997, FLAC (tracks+.cue), lossless", - wantArtist: "Diana King", - wantYear: 1997, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Reggae dawn penn", - title: "(Reggae) [CD] Dawn Penn - Come Again [1996] - 1996, FLAC (tracks+.cue), lossless", - wantArtist: "Dawn Penn", - wantYear: 1996, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Reggae ska Bob Marley", - title: "(Reggae, Ska) [CD] Bob Marley & The Wailers - 3 альбома - (1973-1980), FLAC (tracks+.cue), lossless", - wantArtist: "Bob Marley & The Wailers", - wantYear: 1973, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Reggae Bob Marley collection", - title: "(Reggae) [CD] Bob Marley & The Wailers - коллекция 1970-2012 (86 альбомов), FLAC (image+.cue, tracks+.cue), lossless", - wantArtist: "Bob Marley & The Wailers", - wantYear: 1970, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Reggae rock ska", - title: "(Reggae Rock, Ska) [CD] The English Beat - Special Beat Service - 1986, FLAC (tracks+.cue), lossless", - wantArtist: "The English Beat", - wantYear: 1986, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Reggae VA celebration", - title: "(Reggae, Reggae-Pop, Ragga, Euro-House) [CD] VA - Reggae Celebration '97 Vol. 1 - 1997, FLAC (tracks+.cue), lossless", - wantArtist: "VA", - wantYear: 1997, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Reggae big youth", - title: "(Reggae) [CD] Big Youth - Natty Universal Dread 1973-1979 - 2000, FLAC (tracks+.cue), lossless", - wantArtist: "Big Youth", - wantYear: 2000, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Reggae barrington levy multi-CD", - title: "(Reggae) [CD] Barrington Levy - Sweet Reggae Music 1979-84 (2 CD) - 2012, FLAC (tracks+.cue), lossless", - wantArtist: "Barrington Levy", - wantYear: 2012, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Sega-reggae elijah", - title: "(Sega-Reggae) [CD] ELIJAH - Luveologist - 2006, FLAC (tracks+.cue), lossless", - wantArtist: "ELIJAH", - wantYear: 2006, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Reggae UB40 ultimate edition", - title: "(Reggae) [WEB] UB40 - UB45 [Ultimate Edition] - 2024, FLAC (tracks), lossless", - wantArtist: "UB40", - wantYear: 2024, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/rock.go b/internal/tracker/rutracker/parser/rock.go deleted file mode 100644 index 03fad95..0000000 --- a/internal/tracker/rutracker/parser/rock.go +++ /dev/null @@ -1,38 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type RockParser struct { - BaseParser -} - -func NewRockParser() *RockParser { - return &RockParser{} -} - -func (p *RockParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - if len(r.Genres) == 0 { - r.Genres = []string{"Rock"} - } - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/rock_test.go b/internal/tracker/rutracker/parser/rock_test.go deleted file mode 100644 index 6bcf505..0000000 --- a/internal/tracker/rutracker/parser/rock_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestRockParser(t *testing.T) { - p := NewRockParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantBitrate string - wantParseOK bool - }{ - { - name: "Blues rock single", - title: "(Rock, Blues Rock) [WEB] The Rolling Stones - In The Stars [Single] - 2026, FLAC (tracks), lossless", - wantArtist: "The Rolling Stones", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantType: release.TypeSingle, - wantParseOK: true, - }, - { - name: "Hard rock single", - title: "(Hard Rock) [WEB] J.R. Blackmore - Moments Of Magic (Single) - 2012, FLAC (tracks), lossless", - wantArtist: "J.R. Blackmore", - wantYear: 2012, - wantFormat: release.FormatFLAC, - wantType: release.TypeSingle, - wantParseOK: true, - }, - { - name: "Psychedelic rock collection", - title: "(Psychedelic Rock, Hard Rock, Blues Rock, Progressive Rock) [CD] Atomic Rooster - Collection Albums 1970-1973 (8 CD), FLAC (image+.cue), lossless", - wantArtist: "Atomic Rooster", - wantYear: 1970, - wantFormat: release.FormatFLAC, - wantType: release.TypeCollection, - wantParseOK: true, - }, - { - name: "Rock discography", - title: "(Rock) [CD] Voice Of The Beehive - Discography - 1988-2022 (18 releases), FLAC (tracks+.cue), lossless", - wantArtist: "Voice Of The Beehive", - wantYear: 1988, - wantFormat: release.FormatFLAC, - wantType: release.TypeDiscography, - wantParseOK: true, - }, - { - name: "Indie rock re-release", - title: "(Indie Rock / Indie Pop) [WEB] Easy - Magic Seed - 1990, FLAC (tracks), lossless(Re-release)", - wantArtist: "Easy", - wantYear: 1990, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Progressive rock album", - title: "(Progressive Rock) [WEB] Os Mutantes - De Volta Ao Planeta Dos Mutantes - 2006, FLAC (tracks), lossless", - wantArtist: "Os Mutantes", - wantYear: 2006, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Hard rock reissue", - title: "(Hard Rock) [CD] Gene Simmons - Gene Simmons - 1978 (1991), FLAC (tracks+.cue), lossless", - wantArtist: "Gene Simmons", - wantYear: 1978, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Pop rock collection", - title: "(Pop Rock) [CD] Sneakers (with Sanne Salomonsen) - Collection (1980-1997) (4 releases), FLAC (image+.cue), lossless", - wantArtist: "Sneakers (with Sanne Salomonsen)", - wantYear: 1980, - wantFormat: release.FormatFLAC, - wantType: release.TypeCollection, - wantParseOK: true, - }, - { - name: "Southern rock album", - title: "(Southern Rock, Hard Rock, Blues Rock) [WEB] The Cold Stares - Texas - 2026, FLAC (tracks), lossless", - wantArtist: "The Cold Stares", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Hard rock 70s style", - title: "(Hard Rock, 70's) [WEB] Lynx - Trinity of Suns - 2026, FLAC (tracks), lossless", - wantArtist: "Lynx", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/shanson.go b/internal/tracker/rutracker/parser/shanson.go deleted file mode 100644 index c779cef..0000000 --- a/internal/tracker/rutracker/parser/shanson.go +++ /dev/null @@ -1,38 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type ShansonParser struct { - BaseParser -} - -func NewShansonParser() *ShansonParser { - return &ShansonParser{} -} - -func (p *ShansonParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - if len(r.Genres) == 0 { - r.Genres = []string{"Shanson"} - } - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.ReleaseCount = p.ExtractReleaseCount(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/shanson_test.go b/internal/tracker/rutracker/parser/shanson_test.go deleted file mode 100644 index 2701278..0000000 --- a/internal/tracker/rutracker/parser/shanson_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestShansonParser(t *testing.T) { - p := NewShansonParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantBitrate string - wantParseOK bool - }{ - { - name: "French shanson VA multi-CD", - title: "(Shanson) [4 CD] VA - Chansons Francaises - 2011, FLAC (image+.cue), lossless", - wantArtist: "VA", - wantYear: 2011, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Retro shanson album", - title: "(Retro-Shanson) [CD] Биртман - Следы от компота - 2015, FLAC (tracks+.cue), lossless", - wantArtist: "Биртман", - wantYear: 2015, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Pop shanson collection", - title: "(Pop, Shanson) Sylvie Vartan - Best Artist Collection - 198?, APE (tracks+.cue) lossless", - wantArtist: "Sylvie Vartan", - wantFormat: release.FormatAPE, - wantType: release.TypeCollection, - wantParseOK: true, - }, - { - name: "French shanson Piaf", - title: "(French Shanson) Jil Aigrot - Words Of Love: The Voice of Edith Piaf in the Award-winning Film La Vie En Rose - 2008, APE (image+.cue), lossless", - wantArtist: "Jil Aigrot", - wantYear: 2008, - wantFormat: release.FormatAPE, - wantParseOK: true, - }, - { - name: "Pop shanson Joe Dassin best of", - title: "(Pop/Shanson) Joe Dassin - Greatest Hits (2 CDs SET DIGIPACK) - 2007, FLAC (image + .cue), lossless", - wantArtist: "Joe Dassin", - wantYear: 2007, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Shanson Adamo WavPack", - title: "(Shanson) Salvatore Adamo - L'essentiel - 2003, WAVPack (image+.cue), lossless", - wantArtist: "Salvatore Adamo", - wantYear: 2003, - wantFormat: release.FormatWavPack, - wantParseOK: true, - }, - { - name: "Lounge shanson french pop", - title: "(Lounge, Shanson, French-Pop) Helena Noguerra - Nee Dans La Nature - 2004, APE (image + .cue), lossless", - wantArtist: "Helena Noguerra", - wantYear: 2004, - wantFormat: release.FormatAPE, - wantParseOK: true, - }, - { - name: "Shanson VA blatnaya", - title: "(Shanson) [CD] VA - Блатная Империя 100 лучших Хитов - 2007, FLAC (tracks), lossless", - wantArtist: "VA", - wantYear: 2007, - wantFormat: release.FormatFLAC, - wantType: release.TypeCompilation, - wantParseOK: true, - }, - { - name: "Bard song collection", - title: "(Авторская песня) [CD] Булат Окуджава - Коллекция (20 CD) - 1967-2001, FLAC (image+.cue), lossless", - wantArtist: "Булат Окуджава", - wantYear: 1967, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "Russian shanson discography MP3", - title: "(Шансон) Михаил Круг - Дискография (34 альбома) - 1994-2009, MP3, 192-320 kbps", - wantArtist: "Михаил Круг", - wantYear: 1994, - wantFormat: release.FormatMP3, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantType != release.TypeUnknown && r.Type != tt.wantType { - t.Errorf("Type = %v, want %v", r.Type, tt.wantType) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/soundtracks.go b/internal/tracker/rutracker/parser/soundtracks.go deleted file mode 100644 index 8cd3d7f..0000000 --- a/internal/tracker/rutracker/parser/soundtracks.go +++ /dev/null @@ -1,34 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type SoundtracksParser struct { - BaseParser -} - -func NewSoundtracksParser() *SoundtracksParser { - return &SoundtracksParser{} -} - -func (p *SoundtracksParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - r.Type = release.TypeSoundtrack - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Bitrate = p.ExtractBitrate(title) - r.Source = p.ExtractSource(title) - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/soundtracks_test.go b/internal/tracker/rutracker/parser/soundtracks_test.go deleted file mode 100644 index 73131bb..0000000 --- a/internal/tracker/rutracker/parser/soundtracks_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestSoundtracksParser(t *testing.T) { - p := NewSoundtracksParser() - - tests := []struct { - name string - title string - wantArtist string - wantAlbum string - wantYear int - wantFormat release.AudioFormat - wantType release.Type - wantBitrate string - wantParseOK bool - }{ - { - name: "Game score MP3", - title: "(Score) Yoann Laulan - Cinderia (Original Game Soundtrack) - 2026, MP3, 320 kbps", - wantArtist: "Yoann Laulan", - wantYear: 2026, - wantFormat: release.FormatMP3, - wantType: release.TypeSoundtrack, - wantBitrate: "320 kbps", - wantParseOK: true, - }, - { - name: "Synthwave game soundtrack", - title: "(Synthwave, Dark Synth, Retrowave) VA - Tackle for Loss Official Videogame Soundtrack - 2026, MP3, 320 kbps", - wantArtist: "VA", - wantYear: 2026, - wantType: release.TypeSoundtrack, - wantParseOK: true, - }, - { - name: "Yakuza game OST collection", - title: "(Score / Soundtrack) Yakuza Original Soundtracks (39 albums) (SEGA, VA) - 2007-2026, MP3 (tracks), 320 kbps", - wantYear: 2007, - wantType: release.TypeSoundtrack, - wantParseOK: true, - }, - { - name: "Film score CD FLAC", - title: "(Score) [CD] Jonny Greenwood - One Battle After Another (Original Motion Picture Soundtrack) - 2025, FLAC (image+.cue), lossless", - wantArtist: "Jonny Greenwood", - wantYear: 2025, - wantFormat: release.FormatFLAC, - wantType: release.TypeSoundtrack, - wantParseOK: true, - }, - { - name: "One Piece collection", - title: "(Score) VA - One Piece Soundtrack Collection (4 releases) - 2023-2026, MP3 (tracks), 320 kbps", - wantArtist: "VA", - wantYear: 2023, - wantType: release.TypeSoundtrack, - wantParseOK: true, - }, - { - name: "Life is Strange OST collection", - title: "(Score/Soundtrack/OST) Jonathan Morali - Life is Strange Collection (8 CD) - 2016-2026, MP3, 320 kbps", - wantArtist: "Jonathan Morali", - wantYear: 2016, - wantType: release.TypeSoundtrack, - wantParseOK: true, - }, - { - name: "TR24 game soundtrack WEB", - title: "[TR24][OF][GM] N.J. Apostol - Routine Original Soundtrack - 2026 (Score), FLAC (tracks), lossless", - wantArtist: "N.J. Apostol", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantType: release.TypeSoundtrack, - wantParseOK: true, - }, - { - name: "Hans Zimmer F1 film", - title: "(Score, Soundtrack) [CD] Hans Zimmer - F1 The Movie - 2025, FLAC (tracks+.cue), lossless", - wantArtist: "Hans Zimmer", - wantYear: 2025, - wantFormat: release.FormatFLAC, - wantType: release.TypeSoundtrack, - wantParseOK: true, - }, - { - name: "Stranger Things Netflix", - title: "(Soundtrack) [CD] VA - Stranger Things Soundtrack from the Netflix Series Season 5 - 2026, FLAC (tracks+.cue), lossless", - wantArtist: "VA", - wantYear: 2026, - wantFormat: release.FormatFLAC, - wantType: release.TypeSoundtrack, - wantParseOK: true, - }, - { - name: "Last of Us HBO TR24", - title: "[TR24][OF][TV] Gustavo Santaolalla - The Last of Us Soundtrack from HBO Original Series - 2023 (Score/Soundtrack)", - wantArtist: "Gustavo Santaolalla", - wantYear: 2023, - wantType: release.TypeSoundtrack, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if r.Type != release.TypeSoundtrack { - t.Errorf("Type = %v, want Soundtrack", r.Type) - } - - if tt.wantBitrate != "" && r.Bitrate != tt.wantBitrate { - t.Errorf("Bitrate = %q, want %q", r.Bitrate, tt.wantBitrate) - } - }) - } -} diff --git a/internal/tracker/rutracker/parser/vinyl_digitization.go b/internal/tracker/rutracker/parser/vinyl_digitization.go deleted file mode 100644 index 7a3d3f3..0000000 --- a/internal/tracker/rutracker/parser/vinyl_digitization.go +++ /dev/null @@ -1,42 +0,0 @@ -package parser - -import "homelab.lan/music-agregator/internal/release" - -type VinylDigitizationParser struct { - BaseParser -} - -func NewVinylDigitizationParser() *VinylDigitizationParser { - return &VinylDigitizationParser{} -} - -func (p *VinylDigitizationParser) Parse(title string) *release.Release { - r := p.NewRelease(title) - - r.Genres = p.ExtractGenres(title) - r.Type = p.DetectType(title) - r.Year, r.YearEnd = p.ExtractYearRange(title) - r.Format = p.ExtractFormat(title) - r.Source = release.SourceVinyl - r.RipType = p.ExtractRipType(title) - r.Tags = p.ExtractSpecialTags(title) - r.Label = p.ExtractLabel(title) - r.CatalogNum = p.ExtractCatalogNum(title) - r.Artist, r.Album = p.ExtractArtistAlbum(title) - r.BitDepth, r.SampleRate = p.ExtractHiRes(title) - - if r.Format == release.FormatUnknown { - r.Format = release.FormatFLAC - } - r.Bitrate = "lossless" - - if condMatch := vinylConditionPattern.FindStringSubmatch(title); len(condMatch) >= 2 { - r.Tags = append(r.Tags, "Vinyl:"+condMatch[1]) - } - - if r.Artist == "" { - p.AddError(r, "failed to extract artist") - } - - return r -} diff --git a/internal/tracker/rutracker/parser/vinyl_digitization_test.go b/internal/tracker/rutracker/parser/vinyl_digitization_test.go deleted file mode 100644 index 9da8644..0000000 --- a/internal/tracker/rutracker/parser/vinyl_digitization_test.go +++ /dev/null @@ -1,147 +0,0 @@ -package parser - -import ( - "testing" - - "homelab.lan/music-agregator/internal/release" -) - -func TestVinylDigitizationParser(t *testing.T) { - p := NewVinylDigitizationParser() - - tests := []struct { - name string - title string - wantArtist string - wantYear int - wantBitDepth int - wantSampleRate int - wantFormat release.AudioFormat - wantRipType string - wantParseOK bool - }{ - { - name: "standard LP 24/192", - title: "(Pop-Rock/Punk) [LP] [24/192] Сектор Газа - Ядрена вошь - 1990, WavPack (image+.cue)", - wantArtist: "Сектор Газа", - wantYear: 1990, - wantBitDepth: 24, - wantSampleRate: 192000, - wantFormat: release.FormatWavPack, - wantRipType: "image+.cue", - wantParseOK: true, - }, - { - name: "massive vinyl collection", - title: "(Synth-Pop) [LP/12''/10''/7''] [24/96] Depeche Mode - The Vinyl Collection (17 Albums, 66 Singles, 6 Compilations, 51 Bootlegs) (429 Releases) - 1981-2024, FLAC (tracks) lossless", - wantArtist: "Depeche Mode", - wantYear: 1981, - wantParseOK: true, - }, - { - name: "2xLP 32bit", - title: "(Soft Rock, Pop Rock) [2xLP] [32/176.4] Genesis - Turn It On Again - The Hits - 1999(2024,Reissue, 25th anniversary.), WavPack (tracks)", - wantArtist: "Genesis", - wantYear: 1999, - wantBitDepth: 32, - wantSampleRate: 176400, - wantFormat: release.FormatWavPack, - wantParseOK: true, - }, - { - name: "32/384 ultra high res", - title: "(Prog Rock) [LP] [32/384] Emerson, Lake & Palmer-Emerson, Lake & Palmer - 2025 (1970), WavPack (tracks)", - wantYear: 2025, - wantBitDepth: 32, - wantSampleRate: 384000, - wantFormat: release.FormatWavPack, - wantParseOK: true, - }, - { - name: "soul LP", - title: "(Soul, Funk) [LP] [24/192] Curtis Mayfield - Curtis - 1970/2025, FLAC (tracks)", - wantArtist: "Curtis Mayfield", - wantYear: 1970, - wantBitDepth: 24, - wantSampleRate: 192000, - wantFormat: release.FormatFLAC, - wantParseOK: true, - }, - { - name: "16/44 standard", - title: "(Rock) [LP] [16/44] Tony Sheridan - Collection 4LP - 1976-1987, FLAC (image+.cue)", - wantArtist: "Tony Sheridan", - wantYear: 1976, - wantParseOK: true, - }, - { - name: "MFSL pressing", - title: "(Rock, Pop Rock) [LP] [24/96] Fleetwood Mac – Mirage - 1982 (1984 MFSL 1-119), FLAC (tracks)", - wantArtist: "Fleetwood Mac", - wantYear: 1982, - wantParseOK: true, - }, - { - name: "multiple LPs in one", - title: "(Rock) [LP] [24/96] 10cc - 2LP's - 1976, 1977, FLAC (tracks+.cue)", - wantArtist: "10cc", - wantYear: 1976, - wantRipType: "tracks+.cue", - wantParseOK: true, - }, - { - name: "collection from vinyl", - title: "(Progressive Rock) [LP] [24/192] Marillion, Fish - Vinyl Collection - 1982-1994 (6 releases), FLAC (image+.cue)", - wantArtist: "Marillion, Fish", - wantYear: 1982, - wantParseOK: true, - }, - { - name: "Japan vinyl", - title: "(Pop) [LP] [24/96] ABBA - The Album (Original Japan Vinyl) - 1977, FLAC (tracks)", - wantArtist: "ABBA", - wantYear: 1977, - wantBitDepth: 24, - wantSampleRate: 96000, - wantParseOK: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := p.Parse(tt.title) - - if r.ParsedSuccessfully != tt.wantParseOK { - t.Errorf("ParsedSuccessfully = %v, want %v, errors: %v", r.ParsedSuccessfully, tt.wantParseOK, r.ParseErrors) - } - - if tt.wantArtist != "" && r.Artist != tt.wantArtist { - t.Errorf("Artist = %q, want %q", r.Artist, tt.wantArtist) - } - - if tt.wantYear != 0 && r.Year != tt.wantYear { - t.Errorf("Year = %d, want %d", r.Year, tt.wantYear) - } - - if tt.wantBitDepth != 0 && r.BitDepth != tt.wantBitDepth { - t.Errorf("BitDepth = %d, want %d", r.BitDepth, tt.wantBitDepth) - } - - if tt.wantSampleRate != 0 && r.SampleRate != tt.wantSampleRate { - t.Errorf("SampleRate = %d, want %d", r.SampleRate, tt.wantSampleRate) - } - - if tt.wantFormat != release.FormatUnknown && r.Format != tt.wantFormat { - t.Errorf("Format = %v, want %v", r.Format, tt.wantFormat) - } - - if tt.wantRipType != "" && r.RipType != tt.wantRipType { - t.Errorf("RipType = %q, want %q", r.RipType, tt.wantRipType) - } - - if r.Source != release.SourceVinyl { - t.Errorf("Source = %v, want Vinyl", r.Source) - } - }) - } -}