This commit is contained in:
TylerCG 2026-01-08 18:52:06 -05:00
parent f7e4e1766b
commit 9b47d95605
28 changed files with 6846 additions and 442 deletions

View File

@ -48,6 +48,7 @@
"P:\\movies\\Despicable Me 3 (2017)": 690230066, "P:\\movies\\Despicable Me 3 (2017)": 690230066,
"P:\\movies\\Rush (2013)": 913581666, "P:\\movies\\Rush (2013)": 913581666,
"P:\\movies\\The Nun II (2023)": 1057333481, "P:\\movies\\The Nun II (2023)": 1057333481,
"P:\\movies\\The Amityville Horror (2005)": 629562643,
"P:\\movies\\SAW II (2005)": 628644928, "P:\\movies\\SAW II (2005)": 628644928,
"P:\\movies\\Fortress (2021)": 950706654, "P:\\movies\\Fortress (2021)": 950706654,
"P:\\movies\\Cocaine Bear (2023)": 923105028, "P:\\movies\\Cocaine Bear (2023)": 923105028,
@ -142,6 +143,7 @@
"P:\\movies\\The Suicide Squad (2021)": 5220542826, "P:\\movies\\The Suicide Squad (2021)": 5220542826,
"P:\\movies\\What We Do in the Shadows (2014)": 1324961025, "P:\\movies\\What We Do in the Shadows (2014)": 1324961025,
"P:\\movies\\Pok\u00e9mon the Movie - Secrets of the Jungle (2020)": 2970730073, "P:\\movies\\Pok\u00e9mon the Movie - Secrets of the Jungle (2020)": 2970730073,
"P:\\movies\\Predators (2010)": 575097100,
"P:\\movies\\First Shift (2024)": 862392953, "P:\\movies\\First Shift (2024)": 862392953,
"P:\\movies\\Christmas with the Campbells (2022)": 848536671, "P:\\movies\\Christmas with the Campbells (2022)": 848536671,
"P:\\movies\\Chicken Run - Dawn of the Nugget (2023)": 980150751, "P:\\movies\\Chicken Run - Dawn of the Nugget (2023)": 980150751,
@ -314,7 +316,7 @@
"P:\\movies\\Cats & Dogs - The Revenge of Kitty Galore (2010)": 794156004, "P:\\movies\\Cats & Dogs - The Revenge of Kitty Galore (2010)": 794156004,
"P:\\movies\\Instant Family (2018)": 1039796631, "P:\\movies\\Instant Family (2018)": 1039796631,
"P:\\movies\\Predator - Killer of Killers (2025)": 816946827, "P:\\movies\\Predator - Killer of Killers (2025)": 816946827,
"P:\\movies\\Little Fockers (2010)": 523155307, "P:\\movies\\Little Fockers (2010)": 4277077072,
"P:\\movies\\22 Jump Street (2014)": 856601465, "P:\\movies\\22 Jump Street (2014)": 856601465,
"P:\\movies\\Bambi (1942)": 577460194, "P:\\movies\\Bambi (1942)": 577460194,
"P:\\movies\\Sleeping Beauty (1959)": 1258101653, "P:\\movies\\Sleeping Beauty (1959)": 1258101653,
@ -447,7 +449,7 @@
"P:\\movies\\The Theory of Everything (2014)": 1014618750, "P:\\movies\\The Theory of Everything (2014)": 1014618750,
"P:\\movies\\Hotel for the Holidays (2022)": 813498323, "P:\\movies\\Hotel for the Holidays (2022)": 813498323,
"P:\\movies\\Tomorrowland (2015)": 914578217, "P:\\movies\\Tomorrowland (2015)": 914578217,
"P:\\movies\\Scott Pilgrim vs. the World (2010)": 5104233824, "P:\\movies\\Scott Pilgrim vs. the World (2010)": 2890731039,
"P:\\movies\\Reign of the Supermen (2019)": 786280131, "P:\\movies\\Reign of the Supermen (2019)": 786280131,
"P:\\movies\\The Bricklayer (2023)": 1060158754, "P:\\movies\\The Bricklayer (2023)": 1060158754,
"P:\\movies\\Sinners (2025)": 2230413475, "P:\\movies\\Sinners (2025)": 2230413475,
@ -527,6 +529,7 @@
"P:\\movies\\Bill and Ted's Bogus Journey (1991)": 1873085140, "P:\\movies\\Bill and Ted's Bogus Journey (1991)": 1873085140,
"P:\\movies\\Honey Don't! (2025)": 861699209, "P:\\movies\\Honey Don't! (2025)": 861699209,
"P:\\movies\\Honey, I Blew Up the Kid (1992)": 788453313, "P:\\movies\\Honey, I Blew Up the Kid (1992)": 788453313,
"P:\\movies\\AJR - Somewhere in the sky (2026)": 15484584956,
"P:\\movies\\Cyrano (2021)": 1186014065, "P:\\movies\\Cyrano (2021)": 1186014065,
"P:\\movies\\The Dark Tower (2017)": 960436348, "P:\\movies\\The Dark Tower (2017)": 960436348,
"P:\\movies\\Good Boy! (2003)": 844713316, "P:\\movies\\Good Boy! (2003)": 844713316,
@ -613,10 +616,12 @@
"P:\\movies\\Safe House (2012)": 787215510, "P:\\movies\\Safe House (2012)": 787215510,
"P:\\movies\\Pok\u00e9mon Detective Pikachu (2019)": 2033856759, "P:\\movies\\Pok\u00e9mon Detective Pikachu (2019)": 2033856759,
"P:\\movies\\Superman - Red Son (2020)": 811801544, "P:\\movies\\Superman - Red Son (2020)": 811801544,
"P:\\movies\\Pirates of the Caribbean - Dead Man's Chest (2006)": 3785474444, "P:\\movies\\High Rollers (2025)": 976326920,
"P:\\movies\\Pirates of the Caribbean - Dead Man's Chest (2006)": 5677959143,
"P:\\movies\\Waitress (2007)": 1008081415, "P:\\movies\\Waitress (2007)": 1008081415,
"P:\\movies\\Chef (2014)": 854812021, "P:\\movies\\Chef (2014)": 854812021,
"P:\\movies\\Spider-Man (2002)": 1979695887, "P:\\movies\\Spider-Man (2002)": 1979695887,
"P:\\movies\\Five Nights at Freddy's 2 (2025)": 1003470675,
"P:\\movies\\Patch Adams (1998)": 1017725633, "P:\\movies\\Patch Adams (1998)": 1017725633,
"P:\\movies\\The Conjuring - Last Rites (2025)": 1307715145, "P:\\movies\\The Conjuring - Last Rites (2025)": 1307715145,
"P:\\movies\\Theater Camp (2023)": 891947338, "P:\\movies\\Theater Camp (2023)": 891947338,
@ -653,7 +658,7 @@
"P:\\movies\\Transporter 3 (2008)": 629053239, "P:\\movies\\Transporter 3 (2008)": 629053239,
"P:\\movies\\Red Rocket (2021)": 1250775533, "P:\\movies\\Red Rocket (2021)": 1250775533,
"P:\\movies\\Chaos (2005)": 848357261, "P:\\movies\\Chaos (2005)": 848357261,
"P:\\movies\\Superman (2025)": 13124179581, "P:\\movies\\Superman (2025)": 6208410119,
"P:\\movies\\Layer Cake (2004)": 682551661, "P:\\movies\\Layer Cake (2004)": 682551661,
"P:\\movies\\Once Upon a Christmas Miracle (2018)": 819838950, "P:\\movies\\Once Upon a Christmas Miracle (2018)": 819838950,
"P:\\movies\\You Hurt My Feelings (2023)": 895680655, "P:\\movies\\You Hurt My Feelings (2023)": 895680655,
@ -665,7 +670,7 @@
"P:\\movies\\Badland Hunters (2024)": 1049775793, "P:\\movies\\Badland Hunters (2024)": 1049775793,
"P:\\movies\\Gangster No. 1 (2000)": 990977285, "P:\\movies\\Gangster No. 1 (2000)": 990977285,
"P:\\movies\\Am I Racist! (2024)": 837441227, "P:\\movies\\Am I Racist! (2024)": 837441227,
"P:\\movies\\Ferris Bueller's Day Off (1986)": 5980268040, "P:\\movies\\Ferris Bueller's Day Off (1986)": 3533513139,
"P:\\movies\\Margin Call (2011)": 734058146, "P:\\movies\\Margin Call (2011)": 734058146,
"P:\\movies\\When You Finish Saving the World (2023)": 844223076, "P:\\movies\\When You Finish Saving the World (2023)": 844223076,
"P:\\movies\\The Nines (2007)": 894876710, "P:\\movies\\The Nines (2007)": 894876710,
@ -946,7 +951,7 @@
"P:\\movies\\Ponyo (2008)": 5677514449, "P:\\movies\\Ponyo (2008)": 5677514449,
"P:\\movies\\BASEketball (1998)": 994729312, "P:\\movies\\BASEketball (1998)": 994729312,
"P:\\movies\\Hunter x Hunter - Phantom Rouge (2013)": 864950409, "P:\\movies\\Hunter x Hunter - Phantom Rouge (2013)": 864950409,
"P:\\movies\\Pirates of the Caribbean - At World's End (2007)": 4117617111, "P:\\movies\\Pirates of the Caribbean - At World's End (2007)": 6961369007,
"P:\\movies\\Detective Knight - Rogue (2022)": 1012614253, "P:\\movies\\Detective Knight - Rogue (2022)": 1012614253,
"P:\\movies\\Cliffhanger (1993)": 786895598, "P:\\movies\\Cliffhanger (1993)": 786895598,
"P:\\movies\\Slap Shot (1977)": 910196582, "P:\\movies\\Slap Shot (1977)": 910196582,
@ -1003,9 +1008,11 @@
"P:\\movies\\Waterworld (1995)": 1069371604, "P:\\movies\\Waterworld (1995)": 1069371604,
"P:\\movies\\Longlegs (2024)": 835974578, "P:\\movies\\Longlegs (2024)": 835974578,
"P:\\movies\\Dual (2022)": 909646357, "P:\\movies\\Dual (2022)": 909646357,
"P:\\movies\\xXx - Return of Xander Cage (2017)": 9537340240, "P:\\movies\\xXx - Return of Xander Cage (2017)": 3563512464,
"P:\\movies\\Robin Williams - Come Inside My Mind (2018)": 1021217913,
"P:\\movies\\Brave (2012)": 629765937, "P:\\movies\\Brave (2012)": 629765937,
"P:\\movies\\Batman and Harley Quinn (2017)": 575516986, "P:\\movies\\Batman and Harley Quinn (2017)": 575516986,
"P:\\movies\\Hunting Season (2025)": 966911333,
"P:\\movies\\Last Flag Flying (2017)": 947531807, "P:\\movies\\Last Flag Flying (2017)": 947531807,
"P:\\movies\\A Long Way Down (2014)": 792782735, "P:\\movies\\A Long Way Down (2014)": 792782735,
"P:\\movies\\Shin Godzilla (2016)": 1951714489, "P:\\movies\\Shin Godzilla (2016)": 1951714489,
@ -1131,7 +1138,7 @@
"P:\\movies\\Ghostbusters (1984)": 1756467527, "P:\\movies\\Ghostbusters (1984)": 1756467527,
"P:\\movies\\This Means War (2012)": 734559917, "P:\\movies\\This Means War (2012)": 734559917,
"P:\\movies\\Reunion (2024)": 909882084, "P:\\movies\\Reunion (2024)": 909882084,
"P:\\movies\\The Losers (2010)": 5151113302, "P:\\movies\\The Losers (2010)": 2964760220,
"P:\\movies\\Fighting with My Family (2019)": 964548802, "P:\\movies\\Fighting with My Family (2019)": 964548802,
"P:\\movies\\Strays (2023)": 910698288, "P:\\movies\\Strays (2023)": 910698288,
"P:\\movies\\Hansan - Rising Dragon (2022)": 1253568599, "P:\\movies\\Hansan - Rising Dragon (2022)": 1253568599,
@ -1235,19 +1242,19 @@
"P:\\movies\\A Fantastic Fear of Everything (2012)": 786055829, "P:\\movies\\A Fantastic Fear of Everything (2012)": 786055829,
"P:\\movies\\Green Room (2015)": 737645792, "P:\\movies\\Green Room (2015)": 737645792,
"P:\\movies\\Rise of the Guardians (2012)": 733537206, "P:\\movies\\Rise of the Guardians (2012)": 733537206,
"P:\\movies\\The Roundup (2022)": 5716789610, "P:\\movies\\The Roundup (2022)": 2397207186,
"P:\\movies\\The Great Wall (2016)": 794233234, "P:\\movies\\The Great Wall (2016)": 794233234,
"P:\\movies\\Green Day - 20 Years of American Idiot (2024)": 1064467825, "P:\\movies\\Green Day - 20 Years of American Idiot (2024)": 1064467825,
"P:\\movies\\Sorry to Bother You (2018)": 981546629, "P:\\movies\\Sorry to Bother You (2018)": 981546629,
"P:\\movies\\Merry In-Laws (2012)": 849094480, "P:\\movies\\Merry In-Laws (2012)": 849094480,
"P:\\movies\\Black Clover - Sword of the Wizard King (2023)": 2792853722, "P:\\movies\\Black Clover - Sword of the Wizard King (2023)": 2792853722,
"P:\\movies\\Rocky V (1990)": 683775581, "P:\\movies\\Rocky V (1990)": 683775581,
"P:\\movies\\Starship Troopers (1997)": 7366521230, "P:\\movies\\Starship Troopers (1997)": 5339159761,
"P:\\movies\\Megamind (2010)": 629041367, "P:\\movies\\Megamind (2010)": 629041367,
"P:\\movies\\Mad God (2021)": 809156360, "P:\\movies\\Mad God (2021)": 809156360,
"P:\\movies\\The Other Zoey (2023)": 885187485, "P:\\movies\\The Other Zoey (2023)": 885187485,
"P:\\movies\\The Hunt (2020)": 866933622, "P:\\movies\\The Hunt (2020)": 866933622,
"P:\\movies\\Pirates of the Caribbean - Dead Men Tell No Tales (2017)": 3171926078, "P:\\movies\\Pirates of the Caribbean - Dead Men Tell No Tales (2017)": 3600745878,
"P:\\movies\\Spanglish (2004)": 996868451, "P:\\movies\\Spanglish (2004)": 996868451,
"P:\\movies\\Holes (2003)": 1674984992, "P:\\movies\\Holes (2003)": 1674984992,
"P:\\movies\\Big Trouble (2002)": 762439934, "P:\\movies\\Big Trouble (2002)": 762439934,
@ -1486,7 +1493,7 @@
"P:\\movies\\I am Legend (2007)": 472399898, "P:\\movies\\I am Legend (2007)": 472399898,
"P:\\movies\\Seven Psychopaths (2012)": 787315239, "P:\\movies\\Seven Psychopaths (2012)": 787315239,
"P:\\movies\\Ron's Gone Wrong (2021)": 1027827258, "P:\\movies\\Ron's Gone Wrong (2021)": 1027827258,
"P:\\movies\\Paradise Records (2025)": 2155834535, "P:\\movies\\Paradise Records (2025)": 1051339618,
"P:\\movies\\The Nun (2018)": 876835728, "P:\\movies\\The Nun (2018)": 876835728,
"P:\\movies\\Mars Attacks! (1996)": 974084293, "P:\\movies\\Mars Attacks! (1996)": 974084293,
"P:\\movies\\How the Grinch Stole Christmas (2000)": 3268490830, "P:\\movies\\How the Grinch Stole Christmas (2000)": 3268490830,
@ -1579,8 +1586,9 @@
"P:\\movies\\The Phoenician Scheme (2025)": 2661907395, "P:\\movies\\The Phoenician Scheme (2025)": 2661907395,
"P:\\movies\\Cinderella (2021)": 1087262833, "P:\\movies\\Cinderella (2021)": 1087262833,
"P:\\movies\\The Lego Movie 2 The Second Part (2019)": 950172844, "P:\\movies\\The Lego Movie 2 The Second Part (2019)": 950172844,
"P:\\movies\\Meet the Fockers (2004)": 1495880216, "P:\\movies\\Meet the Fockers (2004)": 5949252474,
"P:\\movies\\The Bad Batch (2017)": 926871424, "P:\\movies\\The Bad Batch (2017)": 926871424,
"P:\\movies\\Forever Young (1992)": 977859032,
"P:\\movies\\Halloweentown (1998)": 810513129, "P:\\movies\\Halloweentown (1998)": 810513129,
"P:\\movies\\Silent Night (2023)": 1003667495, "P:\\movies\\Silent Night (2023)": 1003667495,
"P:\\movies\\Bridget Jones's Diary (2001)": 925436159, "P:\\movies\\Bridget Jones's Diary (2001)": 925436159,
@ -1655,7 +1663,7 @@
"P:\\movies\\Vacation (2015)": 882534348, "P:\\movies\\Vacation (2015)": 882534348,
"P:\\movies\\Canadian Bacon (1995)": 915905234, "P:\\movies\\Canadian Bacon (1995)": 915905234,
"P:\\movies\\Blue Beetle (2023)": 1949820350, "P:\\movies\\Blue Beetle (2023)": 1949820350,
"P:\\movies\\The Baker (2023)": 5497792586, "P:\\movies\\The Baker (2023)": 816561611,
"P:\\movies\\Jurassic Park (1993)": 793306783, "P:\\movies\\Jurassic Park (1993)": 793306783,
"P:\\movies\\Code 3 (2025)": 962773414, "P:\\movies\\Code 3 (2025)": 962773414,
"P:\\movies\\Memory (2022)": 1098424408, "P:\\movies\\Memory (2022)": 1098424408,
@ -1685,7 +1693,7 @@
"P:\\movies\\S.W.A.T. (2019)": 1056909910, "P:\\movies\\S.W.A.T. (2019)": 1056909910,
"P:\\movies\\The Persian Version (2023)": 1036376746, "P:\\movies\\The Persian Version (2023)": 1036376746,
"P:\\movies\\Bambi II (2006)": 643557348, "P:\\movies\\Bambi II (2006)": 643557348,
"P:\\movies\\Pirates of the Caribbean - The Curse of the Black Pearl (2003)": 6749680201, "P:\\movies\\Pirates of the Caribbean - The Curse of the Black Pearl (2003)": 6164584510,
"P:\\movies\\Ordinary World (2016)": 1414665447, "P:\\movies\\Ordinary World (2016)": 1414665447,
"P:\\movies\\Bad Boys for Life (2020)": 1193361085, "P:\\movies\\Bad Boys for Life (2020)": 1193361085,
"P:\\movies\\Pok\u00e9mon - Lucario and the Mystery of Mew (2005)": 2253435788, "P:\\movies\\Pok\u00e9mon - Lucario and the Mystery of Mew (2005)": 2253435788,
@ -1769,7 +1777,7 @@
"P:\\movies\\The Bikeriders (2024)": 1121777975, "P:\\movies\\The Bikeriders (2024)": 1121777975,
"P:\\movies\\Stuber (2019)": 861709290, "P:\\movies\\Stuber (2019)": 861709290,
"P:\\movies\\John Wick - Chapter 4 (2023)": 4680013949, "P:\\movies\\John Wick - Chapter 4 (2023)": 4680013949,
"P:\\movies\\Pirates of the Caribbean - On Stranger Tides (2011)": 2317926572, "P:\\movies\\Pirates of the Caribbean - On Stranger Tides (2011)": 4297016060,
"P:\\movies\\Ghost (1990)": 872499373, "P:\\movies\\Ghost (1990)": 872499373,
"P:\\movies\\Confessions of a Sociopathic Social Climber (2005)": 820262358, "P:\\movies\\Confessions of a Sociopathic Social Climber (2005)": 820262358,
"P:\\movies\\Fatherhood (2021)": 1050118813, "P:\\movies\\Fatherhood (2021)": 1050118813,
@ -2135,6 +2143,7 @@
"P:\\movies\\Beauty and the Beast - Extended Edition (1991)": 575996197, "P:\\movies\\Beauty and the Beast - Extended Edition (1991)": 575996197,
"P:\\movies\\R.I.P.D. 2 - Rise of the Damned (2022)": 921792650, "P:\\movies\\R.I.P.D. 2 - Rise of the Damned (2022)": 921792650,
"P:\\movies\\Couples Retreat (2009)": 787204478, "P:\\movies\\Couples Retreat (2009)": 787204478,
"P:\\movies\\The Wedding Date (2005)": 857810936,
"P:\\movies\\The Girl Next Door UNRATED (2004)": 787262062, "P:\\movies\\The Girl Next Door UNRATED (2004)": 787262062,
"P:\\movies\\Downey Wrote That (2025)": 643153092, "P:\\movies\\Downey Wrote That (2025)": 643153092,
"P:\\movies\\The Nutty Professor (1996)": 681729259, "P:\\movies\\The Nutty Professor (1996)": 681729259,
@ -2185,7 +2194,7 @@
"P:\\movies\\Jurassic World - Fallen Kingdom (2018)": 1158321969, "P:\\movies\\Jurassic World - Fallen Kingdom (2018)": 1158321969,
"P:\\movies\\Moonlight (2016)": 851155837, "P:\\movies\\Moonlight (2016)": 851155837,
"P:\\movies\\Barbarella (1968)": 943578060, "P:\\movies\\Barbarella (1968)": 943578060,
"P:\\movies\\Small Soldiers (1998)": 5094073349, "P:\\movies\\Small Soldiers (1998)": 3036317509,
"P:\\movies\\Gran Torino (2008)": 788767144, "P:\\movies\\Gran Torino (2008)": 788767144,
"P:\\movies\\Enchanted (2007)": 955502916, "P:\\movies\\Enchanted (2007)": 955502916,
"P:\\movies\\Nate Bargatze - Hello World (2023)": 587515341, "P:\\movies\\Nate Bargatze - Hello World (2023)": 587515341,
@ -2217,7 +2226,7 @@
"P:\\movies\\Ride 'Em Cowboy (1942)": 826176530, "P:\\movies\\Ride 'Em Cowboy (1942)": 826176530,
"P:\\movies\\South Park the Streaming Wars (2022)": 462635235, "P:\\movies\\South Park the Streaming Wars (2022)": 462635235,
"P:\\movies\\The Quintessential Quintuplets Movie (2022)": 2671283313, "P:\\movies\\The Quintessential Quintuplets Movie (2022)": 2671283313,
"P:\\movies\\John Wick - Chapter 2 (2017)": 6452174217, "P:\\movies\\John Wick - Chapter 2 (2017)": 2910556976,
"P:\\movies\\Hitman Agent 47 (2015)": 586386449, "P:\\movies\\Hitman Agent 47 (2015)": 586386449,
"P:\\movies\\Paul (2011)": 731417836, "P:\\movies\\Paul (2011)": 731417836,
"P:\\movies\\Butter (2012)": 809373699, "P:\\movies\\Butter (2012)": 809373699,
@ -2384,7 +2393,7 @@
"P:\\movies\\Halloweentown II - Kalabar's Revenge (2001)": 1052744699, "P:\\movies\\Halloweentown II - Kalabar's Revenge (2001)": 1052744699,
"P:\\movies\\The Survivors (1983)": 883588130, "P:\\movies\\The Survivors (1983)": 883588130,
"P:\\movies\\A Bad Moms Christmas (2017)": 800570325, "P:\\movies\\A Bad Moms Christmas (2017)": 800570325,
"P:\\movies\\Meet the Parents (2000)": 2131659488, "P:\\movies\\Meet the Parents (2000)": 6321750057,
"P:\\movies\\Slayers (2022)": 849805353, "P:\\movies\\Slayers (2022)": 849805353,
"P:\\movies\\The Good Nurse (2022)": 840206859, "P:\\movies\\The Good Nurse (2022)": 840206859,
"P:\\movies\\The Death and Life of Bobby Z (2007)": 788294208, "P:\\movies\\The Death and Life of Bobby Z (2007)": 788294208,
@ -2478,7 +2487,7 @@
"P:\\movies\\Spider-Man - Far From Home (2019)": 1838406206, "P:\\movies\\Spider-Man - Far From Home (2019)": 1838406206,
"P:\\movies\\Jack Reacher (2012)": 2051597479, "P:\\movies\\Jack Reacher (2012)": 2051597479,
"P:\\movies\\The Meg (2018)": 1019483886, "P:\\movies\\The Meg (2018)": 1019483886,
"P:\\movies\\Violent Night (2022)": 5106244959, "P:\\movies\\Violent Night (2022)": 1906909883,
"P:\\movies\\House of Gucci (2021)": 1519261888, "P:\\movies\\House of Gucci (2021)": 1519261888,
"P:\\movies\\Mulholland Falls (1996)": 852069037, "P:\\movies\\Mulholland Falls (1996)": 852069037,
"P:\\movies\\The Golden Child (1986)": 833761735, "P:\\movies\\The Golden Child (1986)": 833761735,
@ -2501,7 +2510,7 @@
"P:\\movies\\The Good, the Bart, and the Loki (2021)": 43984236, "P:\\movies\\The Good, the Bart, and the Loki (2021)": 43984236,
"P:\\movies\\Clerks III (2022)": 963907340, "P:\\movies\\Clerks III (2022)": 963907340,
"P:\\movies\\M3GAN 2.0 (2025)": 1158862629, "P:\\movies\\M3GAN 2.0 (2025)": 1158862629,
"P:\\movies\\Belle (2021)": 6192364946, "P:\\movies\\Belle (2021)": 1967787278,
"P:\\movies\\Minamata (2020)": 1104986233, "P:\\movies\\Minamata (2020)": 1104986233,
"P:\\movies\\Alive (1993)": 1216643767, "P:\\movies\\Alive (1993)": 1216643767,
"P:\\movies\\The Princess and the Frog (2009)": 734834721, "P:\\movies\\The Princess and the Frog (2009)": 734834721,
@ -2510,7 +2519,6 @@
"P:\\movies\\Inside Llewyn Davis (2013)": 849607966, "P:\\movies\\Inside Llewyn Davis (2013)": 849607966,
"P:\\movies\\The Addams Family 2 (2021)": 892624431, "P:\\movies\\The Addams Family 2 (2021)": 892624431,
"P:\\movies\\She's Out of My League (2010)": 682549102, "P:\\movies\\She's Out of My League (2010)": 682549102,
"P:\\movies\\AJR - Somewhere in the sky (2025)": 2919401404,
"P:\\movies\\Minions & More 1 (2022)": 465025396, "P:\\movies\\Minions & More 1 (2022)": 465025396,
"P:\\movies\\Blade Runner 2049 (2017)": 2693638088, "P:\\movies\\Blade Runner 2049 (2017)": 2693638088,
"P:\\movies\\Twilight Saga (2008)": 733993564, "P:\\movies\\Twilight Saga (2008)": 733993564,
@ -2607,7 +2615,7 @@
"P:\\movies\\Youngblood (1986)": 978057487, "P:\\movies\\Youngblood (1986)": 978057487,
"P:\\movies\\Inglourious Basterds (2009)": 2855779029, "P:\\movies\\Inglourious Basterds (2009)": 2855779029,
"P:\\movies\\Black or White (2014)": 911771671, "P:\\movies\\Black or White (2014)": 911771671,
"P:\\movies\\AJR - The Maybe Man Immersive Concert Experience (2024)": 4590528226, "P:\\movies\\AJR - The Maybe Man Immersive Concert Experience (2024)": 4591719568,
"P:\\movies\\Animal House (1978)": 787489930, "P:\\movies\\Animal House (1978)": 787489930,
"P:\\movies\\Cinderella (2015)": 1763561462, "P:\\movies\\Cinderella (2015)": 1763561462,
"P:\\movies\\I'll Be Home for Christmas (1998)": 769320107, "P:\\movies\\I'll Be Home for Christmas (1998)": 769320107,
@ -2630,7 +2638,7 @@
"P:\\movies\\The Master of Disguise (2002)": 1343424184, "P:\\movies\\The Master of Disguise (2002)": 1343424184,
"P:\\movies\\DodgeBall - A True Underdog Story (2004)": 787609348, "P:\\movies\\DodgeBall - A True Underdog Story (2004)": 787609348,
"P:\\movies\\The Emperor's New Groove (2000)": 726824390, "P:\\movies\\The Emperor's New Groove (2000)": 726824390,
"P:\\movies\\John Wick - Chapter 3 - Parabellum (2019)": 7853540695, "P:\\movies\\John Wick - Chapter 3 - Parabellum (2019)": 3857527829,
"P:\\movies\\Aloha (2015)": 849543660, "P:\\movies\\Aloha (2015)": 849543660,
"P:\\movies\\Thumbsucker (2005)": 925537380, "P:\\movies\\Thumbsucker (2005)": 925537380,
"P:\\movies\\Ghosted (2023)": 2318216001, "P:\\movies\\Ghosted (2023)": 2318216001,
@ -2705,6 +2713,7 @@
"P:\\movies\\Palm Springs (2020)": 1609086346, "P:\\movies\\Palm Springs (2020)": 1609086346,
"P:\\movies\\Captain America - The First Avenger (2011)": 1713875628, "P:\\movies\\Captain America - The First Avenger (2011)": 1713875628,
"P:\\movies\\Clear History (2013)": 791420953, "P:\\movies\\Clear History (2013)": 791420953,
"P:\\movies\\Interstate 60 (2002)": 1079307887,
"P:\\movies\\Jeff Dunham - Me the People (2022)": 404337420, "P:\\movies\\Jeff Dunham - Me the People (2022)": 404337420,
"P:\\movies\\Scary Movie 2 (2001)": 794281384, "P:\\movies\\Scary Movie 2 (2001)": 794281384,
"P:\\movies\\About My Father (2023)": 1609106023, "P:\\movies\\About My Father (2023)": 1609106023,

View File

@ -1,322 +1,322 @@
{ {
"P:\\tv\\1883": 4514294832,
"P:\\tv\\1923": 22125507023,
"P:\\tv\\3 Body Problem": 11369334730,
"P:\\tv\\30 Rock (2006)": 81412969909,
"P:\\tv\\Abbott Elementary (2021)": 24595462535,
"P:\\tv\\Adults (2025)": 6845585714,
"P:\\tv\\Adventuring Academy": 62196997373,
"P:\\tv\\Agatha All Along": 3411637969,
"P:\\tv\\Alien - Earth (2025)": 2926145405,
"P:\\tv\\Amazing Stories (2020)": 4281304451,
"P:\\tv\\American Gods (2017)": 43921706762,
"P:\\tv\\American Horror Story": 142468660014,
"P:\\tv\\Andor (2022)": 25679584728,
"P:\\tv\\Arcane (2021)": 19588567847,
"P:\\tv\\Assembly Required (2021)": 5737519036,
"P:\\tv\\Avenue 5": 12572813494,
"P:\\tv\\Bad Monkey": 7767595411,
"P:\\tv\\Ballers": 13002096756,
"P:\\tv\\Band of Brothers (2001)": 15129362120,
"P:\\tv\\Banshee (2013)": 25030541772,
"P:\\tv\\Barry": 31934844666,
"P:\\tv\\BattleBots": 61,
"P:\\tv\\BattleBots (2015)": 69,
"P:\\tv\\Being Human (2011)": 66311454464,
"P:\\tv\\Belgravia - The Next Chapter": 8340040939,
"P:\\tv\\Below Deck": 47516712212,
"P:\\tv\\Below Deck Down Under (2022)": 36006759742,
"P:\\tv\\Below Deck Mediterranean": 39902249615,
"P:\\tv\\Below Deck Sailing Yacht": 12706704039,
"P:\\tv\\Better Call Saul": 31152560439,
"P:\\tv\\Billions": 31141419259,
"P:\\tv\\Billy the Kid": 44803721006,
"P:\\tv\\Black Bird (2022)": 5893929480,
"P:\\tv\\Black Sails (2014)": 11356486450,
"P:\\tv\\Brooklyn Nine Nine": 45722673163,
"P:\\tv\\Bupkis": 13034439710,
"P:\\tv\\Canada's Drag Race": 103586850759,
"P:\\tv\\Canada's Drag Race vs The World": 7844155647,
"P:\\tv\\Catch-22": 7113496871,
"P:\\tv\\Chad Powers (2025)": 2474659236,
"P:\\tv\\Chilling Adventures of Sabrina (2018)": 23147355371,
"P:\\tv\\Chuck": 32193192829,
"P:\\tv\\Citadel": 2339699246,
"P:\\tv\\Citadel - Diana": 13304679453,
"P:\\tv\\Cobra Kai": 39761471967,
"P:\\tv\\Continuum (2012)": 29352883496,
"P:\\tv\\Countdown (2025)": 8935252687,
"P:\\tv\\Counterpart": 4875616955,
"P:\\tv\\Creature Commandos (2024)": 2331424358,
"P:\\tv\\Crowd Control": 9644641207,
"P:\\tv\\Cyberpunk - Edgerunners (2022)": 11313875182,
"P:\\tv\\Daredevil - Born Again (2025)": 7647367391,
"P:\\tv\\Dark Side of the Ring": 11863132534,
"P:\\tv\\Dateline NBC (1992)": 19267231607,
"P:\\tv\\Death and Other Details": 17844763765,
"P:\\tv\\Detroiters (2017)": 33750584701,
"P:\\tv\\Dimension 20": 557729281243,
"P:\\tv\\Dimension 20's Adventuring Party": 12002285238,
"P:\\tv\\Dirk Gently's Holistic Detective Agency (2016)": 11935610182,
"P:\\tv\\Dirty Laundry": 38036591078,
"P:\\tv\\Doctor Who (2005)": 5820708419,
"P:\\tv\\Dopesick": 2571994785,
"P:\\tv\\DOTA - Dragon's Blood (2021)": 12538510766,
"P:\\tv\\Dracula (2020)": 2147285239,
"P:\\tv\\Dune - Prophecy": 3330003290,
"P:\\tv\\Dungeons & Dragons": 6660128393,
"P:\\tv\\Dwight in Shining Armor": 75,
"P:\\tv\\English Teacher": 7603165476,
"P:\\tv\\Euphoria": 40925172559,
"P:\\tv\\Extraordinary": 6934203888,
"P:\\tv\\Extrapolations": 6155965724,
"P:\\tv\\Face Off (2011)": 83155672195,
"P:\\tv\\Fallen (2024)": 4161867429,
"P:\\tv\\Fallout": 19686023936,
"P:\\tv\\Fargo (2014)": 93792752129,
"P:\\tv\\Father Brown": 18896564477,
"P:\\tv\\Fired on Mars (2023)": 3590992124,
"P:\\tv\\Firefly (2002)": 7517428895,
"P:\\tv\\From Dusk Till Dawn - The Series (2014)": 5360771338,
"P:\\tv\\Galavant": 12147863291,
"P:\\tv\\Game Changer": 38317757866,
"P:\\tv\\Game Changers (2024)": 5880504271,
"P:\\tv\\Game Of Thrones": 119681469870,
"P:\\tv\\Gastronauts": 9365810750,
"P:\\tv\\Gen V (2023)": 16871757804,
"P:\\tv\\Ghosts (2019)": 40703143881,
"P:\\tv\\Ghosts (2021)": 4574333812,
"P:\\tv\\Goosebumps (2023)": 8257419062,
"P:\\tv\\Gordon Ramsay's Food Stars (2023)": 6344621632,
"P:\\tv\\Government Cheese (2025)": 15970704500,
"P:\\tv\\Gravity Falls": 31900305156,
"P:\\tv\\Halo": 6961206915,
"P:\\tv\\Harley and the Davidsons": 76,
"P:\\tv\\Harley Quinn": 20857796821,
"P:\\tv\\Harry Potter - Wizards of Baking (2024)": 23545641052,
"P:\\tv\\Haunted Hotel (2025)": 4735071992,
"P:\\tv\\Hawkeye": 13524278345,
"P:\\tv\\Hazbin Hotel (2024)": 10906489515,
"P:\\tv\\Hero Inside (2023)": 7372329680, "P:\\tv\\Hero Inside (2023)": 7372329680,
"P:\\tv\\High Potential": 24339309461, "P:\\tv\\Below Deck": 47516712212,
"P:\\tv\\Hitmen (2020)": 12274410846,
"P:\\tv\\Home Economics": 14315967074,
"P:\\tv\\Home Improvement 1991": 48878774505,
"P:\\tv\\House of Guinness (2025)": 5444928896,
"P:\\tv\\House of the Dragon": 23959073249,
"P:\\tv\\iCarly (2021)": 19966043984,
"P:\\tv\\Impractical Jokers": 13357380400,
"P:\\tv\\In the Dark (2019)": 2555891397,
"P:\\tv\\Ink Master": 23329086486,
"P:\\tv\\Interior Chinatown": 3167640001,
"P:\\tv\\Invincible (2021)": 19742824176,
"P:\\tv\\Ironheart (2025)": 3153557870,
"P:\\tv\\Its Always Sunny in Philadelphia": 84650830434,
"P:\\tv\\Jentry Chau vs. the Underworld (2024)": 1406237358,
"P:\\tv\\Junior Taskmaster (2024)": 4133620030,
"P:\\tv\\Jury Duty": 8010062372,
"P:\\tv\\Kaos": 5164057710,
"P:\\tv\\Kevin Can F-k Himself": 11614889793,
"P:\\tv\\Killer Cakes": 3673781461,
"P:\\tv\\Kim's Convenience": 30475634673,
"P:\\tv\\Kitchen Nightmares UK": 11563663098,
"P:\\tv\\Kitchen Nightmares US": 56092851597,
"P:\\tv\\Knuckles": 2140786440,
"P:\\tv\\Krypton (2018)": 10875524680,
"P:\\tv\\Landman (2024)": 35220290035,
"P:\\tv\\Last Man Standing": 49393251846,
"P:\\tv\\Lawmen - Bass Reeves (2023)": 5363156538,
"P:\\tv\\Lessons in Chemistry (2023)": 5485801173,
"P:\\tv\\Letterkenny": 63,
"P:\\tv\\Life After People (2009)": 45628647899,
"P:\\tv\\Loki": 20082144632,
"P:\\tv\\Love Island (US) (2019)": 20699120877,
"P:\\tv\\Love, Death & Robots (2019)": 8204860116,
"P:\\tv\\Lucky Hank": 7336222432,
"P:\\tv\\Ludwig (2024)": 2670615425,
"P:\\tv\\Made For Love (2021)": 2211136772,
"P:\\tv\\Make Some Noise": 25555591381,
"P:\\tv\\Man Down (2013)": 5077144151,
"P:\\tv\\Married at First Sight (2014)": 30275711911,
"P:\\tv\\Married... with Children (1987)": 64228823786,
"P:\\tv\\Marvel's The Punisher (2017)": 32242478897,
"P:\\tv\\Matlock (2024)": 34470939613,
"P:\\tv\\Mayor of Kingstown (2021)": 65464041666,
"P:\\tv\\Mighty Nein (2025)": 6138965943,
"P:\\tv\\MobLand (2025)": 6622179548,
"P:\\tv\\Modern Family": 82788065200,
"P:\\tv\\Monarch Legacy of Monsters": 18371826949,
"P:\\tv\\Monet's Slumber Party": 8253206091,
"P:\\tv\\Moon Knight": 10976093361,
"P:\\tv\\Mr. & Mrs. Smith (2024)": 5316681916,
"P:\\tv\\Murder She Wrote": 12095973826,
"P:\\tv\\Murderbot (2025)": 18338040970,
"P:\\tv\\Mythic Quest": 16965795814,
"P:\\tv\\New Girl": 40676856398,
"P:\\tv\\Nobody Wants This": 11516933757,
"P:\\tv\\Obi-Wan Kenobi": 13867986923,
"P:\\tv\\One More Time (2024)": 6434473461,
"P:\\tv\\Only Murders in the Building (2021)": 2379838148,
"P:\\tv\\Our Flag Means Death": 2107045664,
"P:\\tv\\Outlander": 27364180668,
"P:\\tv\\Over the Garden Wall": 2937573633,
"P:\\tv\\Pantheon": 13397374449,
"P:\\tv\\Paradise (2025)": 8024209737,
"P:\\tv\\Parks and Recreation": 37277190974,
"P:\\tv\\Parlor Room": 12022280605,
"P:\\tv\\Passion for punchlines": 75514795,
"P:\\tv\\Peacemaker (2022)": 13199970800,
"P:\\tv\\Percy Jackson and the Olympians": 3558450335,
"P:\\tv\\Platonic (2023)": 17488146510,
"P:\\tv\\Pok\u00e9mon Concierge (2023)": 1134616527,
"P:\\tv\\Poppa\u2019s House": 13794748297,
"P:\\tv\\Power (2014)": 20414619656,
"P:\\tv\\Quantum Leap (1989)": 39284023472,
"P:\\tv\\Quantum Leap 2022": 8902776416,
"P:\\tv\\Quiet On Set - The Dark Side Of Kids TV": 12191520028,
"P:\\tv\\Raised by wolves": 9720677524,
"P:\\tv\\Reacher (2022)": 17521873037,
"P:\\tv\\Resident Alien (2021)": 17522605407,
"P:\\tv\\Rick and Morty": 31672318625,
"P:\\tv\\Royal Pains (2009)": 1247586112,
"P:\\tv\\Running Man": 10279755878,
"P:\\tv\\Rupaul's Drag Race": 57149739065,
"P:\\tv\\Rupaul's Drag Race All Stars": 60579323023,
"P:\\tv\\RuPaul's Drag Race Down Under": 27454793482,
"P:\\tv\\Rupaul's Drag Race UK": 110914388896,
"P:\\tv\\Rupaul's Drag Race Vegas Revue": 2532474468,
"P:\\tv\\Rupauls Drag Race UK vs The World": 35504142221,
"P:\\tv\\SAS Rogue Heroes (2022)": 10733559643,
"P:\\tv\\Saving Hope": 33116225358,
"P:\\tv\\Scenes from a Marriage (US)": 12493986505,
"P:\\tv\\Schitt's Creek": 9325109901,
"P:\\tv\\Schmigadoon!": 6206632733,
"P:\\tv\\SCORPION": 54081802764,
"P:\\tv\\Secret Celebrity RuPaul's Drag Race": 4211193920,
"P:\\tv\\Secret Level": 2810124465,
"P:\\tv\\See": 12316511887,
"P:\\tv\\Selfie": 5013734266,
"P:\\tv\\Severance": 15044806873,
"P:\\tv\\She-Hulk Attorney at Law": 10233633417,
"P:\\tv\\Shetland": 18537045340,
"P:\\tv\\Shifting Gears (2025)": 12649531141,
"P:\\tv\\Shoresy": 9701736850,
"P:\\tv\\Shrinking (2023)": 17293593983,
"P:\\tv\\Sh\u014dgun": 20899988683,
"P:\\tv\\Silicon Valley (2014)": 63657428121,
"P:\\tv\\Silo (2023)": 12897630564,
"P:\\tv\\Sirens (2025)": 4246622090,
"P:\\tv\\Smartypants": 15959708127,
"P:\\tv\\Smiling Friends": 5633340834,
"P:\\tv\\Solar Opposites": 1138214210,
"P:\\tv\\Son of Zorn (2016)": 6780978712,
"P:\\tv\\South Park": 70261225261,
"P:\\tv\\Spartacus": 75639017886,
"P:\\tv\\Special Ops Lioness": 9765393961,
"P:\\tv\\Squid Game (2021)": 22082475135,
"P:\\tv\\St. Denis Medical (2024)": 18704263469,
"P:\\tv\\Star Strek Strange New Worlds": 13781151928,
"P:\\tv\\Star Trek Lower Decks": 33090597113,
"P:\\tv\\Star Wars - Skeleton Crew (2024)": 2940779001,
"P:\\tv\\Stargirl": 9507100884,
"P:\\tv\\Station Eleven": 2708694925,
"P:\\tv\\Stranger Things (2016)": 64934698827,
"P:\\tv\\Suits LA (2025)": 22274831381,
"P:\\tv\\Superman and Lois": 44881535930,
"P:\\tv\\Supernatural": 377851589424,
"P:\\tv\\Sweetpea": 2706241673,
"P:\\tv\\Swimming with Sharks": 4426141798,
"P:\\tv\\Taboo (2017)": 19309841226,
"P:\\tv\\Taskmaster": 142193364333,
"P:\\tv\\Taskmaster (CA) (2022)": 2431664380,
"P:\\tv\\Taskmaster (NZ)": 71323320898,
"P:\\tv\\Taskmaster - Champion of Champions": 2700754514,
"P:\\tv\\Taskmaster AU": 20527610746,
"P:\\tv\\Taylor (2025)": 2621206209,
"P:\\tv\\Ted (2024)": 3024624414,
"P:\\tv\\Ted Lasso (2020)": 52046307136,
"P:\\tv\\Terminator Zero": 3384699699,
"P:\\tv\\The 10th Kingdom (2000)": 14174589505,
"P:\\tv\\The Amazing Digital Circus (2023)": 4739070191,
"P:\\tv\\The Bachelor": 40368931577,
"P:\\tv\\The Bachelorette": 9927266246,
"P:\\tv\\The Bear (2022)": 43665628138,
"P:\\tv\\The Big Door Prize": 2314902686,
"P:\\tv\\The Bondsman (2025)": 3112664353,
"P:\\tv\\The Book of Boba Fett": 12039417291,
"P:\\tv\\The Boys": 68010010167,
"P:\\tv\\The Chosen (2019)": 54241850899,
"P:\\tv\\The Closer": 47449608535,
"P:\\tv\\The Consultant (2023)": 74,
"P:\\tv\\The Continental (2023)": 1920206807,
"P:\\tv\\The Day of the Jackal (2024)": 17787097381,
"P:\\tv\\The Dragon Dentist": 11317084093,
"P:\\tv\\The Drew Carey Show (1995)": 70,
"P:\\tv\\The Edge of Sleep": 1358235145,
"P:\\tv\\The Eternaut": 17178505929,
"P:\\tv\\The Falcon and The Winter Soldier (2021)": 11657055937,
"P:\\tv\\The Fall of Diddy (2025)": 2431035593,
"P:\\tv\\The Fall of the House of Usher (2023)": 16454192941,
"P:\\tv\\The Forsytes (2025)": 4034792830,
"P:\\tv\\The Franchise (2024)": 2981270395,
"P:\\tv\\The Gentlemen (2024)": 5224500371,
"P:\\tv\\The Gilded Age": 90505242840,
"P:\\tv\\The Goes Wrong Show (2019)": 3676343887,
"P:\\tv\\The Good Lord Bird (2020)": 5619421375,
"P:\\tv\\The Great (2020)": 22361386693,
"P:\\tv\\The Great British Bake Off": 78,
"P:\\tv\\The IT Crowd (2006)": 9239572772,
"P:\\tv\\The Journal of the Mysterious Creatures (2019)": 92,
"P:\\tv\\The Last of Us": 30545352719,
"P:\\tv\\The Legend of Vox Machina": 25197294503,
"P:\\tv\\The Lord of the Rings - The Rings of Power": 12834498889,
"P:\\tv\\The Mandalorian": 36487773789,
"P:\\tv\\The Morning Show": 94311701751,
"P:\\tv\\The Newsroom": 27756667258,
"P:\\tv\\The Now": 836886747,
"P:\\tv\\The Offer": 9070667475,
"P:\\tv\\The Office (US)": 161867626607,
"P:\\tv\\The Old Man (2022)": 26139845941,
"P:\\tv\\The Originals (2013)": 72912846985,
"P:\\tv\\The Paper (2025)": 8102218176,
"P:\\tv\\The Penguin": 4459075060, "P:\\tv\\The Penguin": 4459075060,
"P:\\tv\\The Pretender": 18425629462, "P:\\tv\\Banshee (2013)": 25030541772,
"P:\\tv\\The Queen's Gambit": 4100494817, "P:\\tv\\Dungeons & Dragons": 6660128393,
"P:\\tv\\The Rain (2018)": 2941174698, "P:\\tv\\Made For Love (2021)": 2211136772,
"P:\\tv\\The Santa Clauses (2022)": 6400385164, "P:\\tv\\Sirens (2025)": 4246622090,
"P:\\tv\\The Second Best Hospital in the Galaxy (2024)": 3636394169, "P:\\tv\\Landman (2024)": 35968103808,
"P:\\tv\\The Split": 7970767632, "P:\\tv\\Last Man Standing": 49393251846,
"P:\\tv\\The Studio (2025)": 11530554023, "P:\\tv\\Alien - Earth (2025)": 2926145405,
"P:\\tv\\The Take": 6020370013, "P:\\tv\\The Big Door Prize": 2314902686,
"P:\\tv\\The Terminal List - Dark Wolf (2025)": 9939046560, "P:\\tv\\Government Cheese (2025)": 15970704500,
"P:\\tv\\The Traitors (US) (2023)": 48149750078, "P:\\tv\\In the Dark (2019)": 2555891397,
"P:\\tv\\The Trunk (2024)": 16810949304,
"P:\\tv\\The Umbrella Academy": 55348092191,
"P:\\tv\\Time Bandits (2024)": 6997478287,
"P:\\tv\\Tires (2024)": 5375794389,
"P:\\tv\\Titans (2018)": 31986198137,
"P:\\tv\\Tokyo Override (2024)": 3802255332,
"P:\\tv\\Tomb Raider - The Legend of Lara Croft": 9341088252,
"P:\\tv\\Tulsa King": 41351406080, "P:\\tv\\Tulsa King": 41351406080,
"P:\\tv\\Twisted Metal (2023)": 12547412897, "P:\\tv\\Dopesick": 2571994785,
"P:\\tv\\Um, Actually": 12360993522, "P:\\tv\\Taylor (2025)": 2621206209,
"P:\\tv\\Unstable": 5444623642, "P:\\tv\\Star Trek Lower Decks": 33090597113,
"P:\\tv\\Utopia (AU)": 8691287022, "P:\\tv\\Face Off (2011)": 83155672195,
"P:\\tv\\Very Important People": 12237876110, "P:\\tv\\Catch-22": 7113496871,
"P:\\tv\\Vice Principals (2016)": 18406955713, "P:\\tv\\Canada's Drag Race": 106759553819,
"P:\\tv\\Vikings (2013)": 194095449878, "P:\\tv\\Over the Garden Wall": 2937573633,
"P:\\tv\\Villainous (2017)": 1961793524, "P:\\tv\\The Traitors (US) (2023)": 48149750078,
"P:\\tv\\Walker": 5492500161, "P:\\tv\\1923": 22125507023,
"P:\\tv\\Wandavision": 10099450034, "P:\\tv\\Loki": 20082144632,
"P:\\tv\\Welcome to Chippendales (2022)": 10423545837, "P:\\tv\\House of the Dragon": 23959073249,
"P:\\tv\\Welcome to Wrexham": 66664948104, "P:\\tv\\The Trunk (2024)": 16810949304,
"P:\\tv\\What If": 21312022582, "P:\\tv\\The Chosen (2019)": 54241850899,
"P:\\tv\\Wildemount Wildlings (2025)": 3348907992, "P:\\tv\\Lucky Hank": 7336222432,
"P:\\tv\\Winning Time - The Rise of the Lakers Dynasty (2022)": 37911197652, "P:\\tv\\Station Eleven": 2708694925,
"P:\\tv\\Kitchen Nightmares UK": 11563663098,
"P:\\tv\\The Good Lord Bird (2020)": 5619421375,
"P:\\tv\\Wolf Pack": 6844099384, "P:\\tv\\Wolf Pack": 6844099384,
"P:\\tv\\WondLa": 1399628000, "P:\\tv\\Below Deck Mediterranean": 40713122628,
"P:\\tv\\Worst Cooks in America (2010)": 22063867049, "P:\\tv\\The Old Man (2022)": 26139845941,
"P:\\tv\\Schitt's Creek": 9325109901,
"P:\\tv\\Mr. & Mrs. Smith (2024)": 5316681916,
"P:\\tv\\Percy Jackson and the Olympians": 3558450335,
"P:\\tv\\Firefly (2002)": 7517428895,
"P:\\tv\\Ballers": 13002096756,
"P:\\tv\\Bupkis": 13034439710,
"P:\\tv\\The Offer": 9070667475,
"P:\\tv\\Life After People (2009)": 45628647899,
"P:\\tv\\The Lord of the Rings - The Rings of Power": 12834498889,
"P:\\tv\\Paradise (2025)": 8024209737,
"P:\\tv\\Nobody Wants This": 11516933757,
"P:\\tv\\Shrinking (2023)": 17293593983,
"P:\\tv\\Hawkeye": 13524278345,
"P:\\tv\\Home Economics": 14315967074,
"P:\\tv\\Time Bandits (2024)": 6997478287,
"P:\\tv\\Lessons in Chemistry (2023)": 5485801173,
"P:\\tv\\1883": 4514294832,
"P:\\tv\\Love, Death & Robots (2019)": 8204860116,
"P:\\tv\\The Legend of Vox Machina": 25197294503,
"P:\\tv\\Harry Potter - Wizards of Baking (2024)": 23545641052,
"P:\\tv\\The Bachelor": 40368931577,
"P:\\tv\\American Horror Story": 142468660014,
"P:\\tv\\Yellowstone (2018)": 89724605866, "P:\\tv\\Yellowstone (2018)": 89724605866,
"P:\\tv\\St. Denis Medical (2024)": 19403375683,
"P:\\tv\\Cobra Kai": 39761471967,
"P:\\tv\\Power (2014)": 20414619656,
"P:\\tv\\The Originals (2013)": 72912846985,
"P:\\tv\\The Edge of Sleep": 1358235145,
"P:\\tv\\3 Body Problem": 11369334730,
"P:\\tv\\New Girl": 40676856398,
"P:\\tv\\Assembly Required (2021)": 5737519036,
"P:\\tv\\30 Rock (2006)": 81412969909,
"P:\\tv\\Rupauls Drag Race UK vs The World": 35504142221,
"P:\\tv\\Daredevil - Born Again (2025)": 7647367391,
"P:\\tv\\Brooklyn Nine Nine": 45722673163,
"P:\\tv\\Taskmaster - Champion of Champions": 2700754514,
"P:\\tv\\Kim's Convenience": 30475634673,
"P:\\tv\\The Office (US)": 161867626607,
"P:\\tv\\Stranger Things (2016)": 66712664909,
"P:\\tv\\Rupaul's Drag Race Vegas Revue": 2532474468,
"P:\\tv\\The Umbrella Academy": 55348092191,
"P:\\tv\\Secret Celebrity RuPaul's Drag Race": 4211193920,
"P:\\tv\\Andor (2022)": 25679584728,
"P:\\tv\\The Bondsman (2025)": 3112664353,
"P:\\tv\\Ghosts (2021)": 4574333812,
"P:\\tv\\Interior Chinatown": 3167640001,
"P:\\tv\\Selfie": 5013734266,
"P:\\tv\\Supernatural": 209274293691,
"P:\\tv\\Superman and Lois": 44881535930,
"P:\\tv\\Black Sails (2014)": 11356486450,
"P:\\tv\\Taskmaster (CA) (2022)": 2431664380,
"P:\\tv\\The Last of Us": 30545352719,
"P:\\tv\\Halo": 6961206915,
"P:\\tv\\Home Improvement 1991": 48878774505,
"P:\\tv\\Detroiters (2017)": 33750584701,
"P:\\tv\\Wildemount Wildlings (2025)": 3348907992,
"P:\\tv\\Terminator Zero": 3384699699,
"P:\\tv\\Um, Actually": 12360993522,
"P:\\tv\\The Rain (2018)": 2941174698,
"P:\\tv\\Harley Quinn": 20857796821,
"P:\\tv\\Lawmen - Bass Reeves (2023)": 5363156538,
"P:\\tv\\Parks and Recreation": 37277190974,
"P:\\tv\\Mythic Quest": 16965795814,
"P:\\tv\\Invincible (2021)": 19742824176,
"P:\\tv\\The Bear (2022)": 43665628138,
"P:\\tv\\Jentry Chau vs. the Underworld (2024)": 1406237358,
"P:\\tv\\Countdown (2025)": 8935252687,
"P:\\tv\\The Great British Bake Off": 78,
"P:\\tv\\Smartypants": 15959708127,
"P:\\tv\\Scenes from a Marriage (US)": 12493986505,
"P:\\tv\\The Franchise (2024)": 2981270395,
"P:\\tv\\Chad Powers (2025)": 2474659236,
"P:\\tv\\Doctor Who (2005)": 5820708419,
"P:\\tv\\Bad Monkey": 7767595411,
"P:\\tv\\Swimming with Sharks": 4426141798,
"P:\\tv\\English Teacher": 7603165476,
"P:\\tv\\Resident Alien (2021)": 17522605407,
"P:\\tv\\Krypton (2018)": 10875524680,
"P:\\tv\\Vikings (2013)": 194095449878,
"P:\\tv\\Arcane (2021)": 19588567847,
"P:\\tv\\Ludwig (2024)": 2670615425,
"P:\\tv\\Canada's Drag Race vs The World": 7844155647,
"P:\\tv\\BattleBots (2015)": 69,
"P:\\tv\\Abbott Elementary (2021)": 24595462535,
"P:\\tv\\Billy the Kid": 44803721006,
"P:\\tv\\Quantum Leap 2022": 8902776416,
"P:\\tv\\Obi-Wan Kenobi": 13867986923,
"P:\\tv\\Matlock (2024)": 34470939613,
"P:\\tv\\The Fall of Diddy (2025)": 2431035593,
"P:\\tv\\Kaos": 5164057710,
"P:\\tv\\Shifting Gears (2025)": 13556293879,
"P:\\tv\\Saving Hope": 33116225358,
"P:\\tv\\Gen V (2023)": 16871757804,
"P:\\tv\\Below Deck Sailing Yacht": 12706704039,
"P:\\tv\\Monarch Legacy of Monsters": 18371826949,
"P:\\tv\\High Potential": 24845798484,
"P:\\tv\\Band of Brothers (2001)": 15129362120,
"P:\\tv\\Quantum Leap (1989)": 39284023472,
"P:\\tv\\Harley and the Davidsons": 76,
"P:\\tv\\Rupaul's Drag Race All Stars": 60579323023,
"P:\\tv\\Amazing Stories (2020)": 4281304451,
"P:\\tv\\Murder She Wrote": 12095973826,
"P:\\tv\\Kitchen Nightmares US": 56092851597,
"P:\\tv\\Game Changer": 38317757866,
"P:\\tv\\Taskmaster AU": 20527610746,
"P:\\tv\\Fallout": 20302317773,
"P:\\tv\\Young Sheldon": 21714069112, "P:\\tv\\Young Sheldon": 21714069112,
"P:\\tv\\Your Honor (2020)": 25879839349 "P:\\tv\\Vice Principals (2016)": 18406955713,
"P:\\tv\\Adventuring Academy": 62196997373,
"P:\\tv\\Solar Opposites": 1138214210,
"P:\\tv\\Pok\u00e9mon Concierge (2023)": 1134616527,
"P:\\tv\\Better Call Saul": 31152560439,
"P:\\tv\\Counterpart": 4875616955,
"P:\\tv\\The Paper (2025)": 8102218176,
"P:\\tv\\Chuck": 32193192829,
"P:\\tv\\The Bachelorette": 9927266246,
"P:\\tv\\Wandavision": 10099450034,
"P:\\tv\\Pantheon": 13397374449,
"P:\\tv\\The Gilded Age": 90505242840,
"P:\\tv\\Gastronauts": 9365810750,
"P:\\tv\\American Gods (2017)": 43921706762,
"P:\\tv\\The IT Crowd (2006)": 9239572772,
"P:\\tv\\Winning Time - The Rise of the Lakers Dynasty (2022)": 37911197652,
"P:\\tv\\Monet's Slumber Party": 8253206091,
"P:\\tv\\Walker": 5492500161,
"P:\\tv\\Stargirl": 9507100884,
"P:\\tv\\House of Guinness (2025)": 5444928896,
"P:\\tv\\Father Brown": 18896564477,
"P:\\tv\\Silo (2023)": 12897630564,
"P:\\tv\\Your Honor (2020)": 25879839349,
"P:\\tv\\Welcome to Wrexham": 66664948104,
"P:\\tv\\Royal Pains (2009)": 1247586112,
"P:\\tv\\The Continental (2023)": 1920206807,
"P:\\tv\\Citadel": 2339699246,
"P:\\tv\\The 10th Kingdom (2000)": 14174589505,
"P:\\tv\\Parlor Room": 12022280605,
"P:\\tv\\Its Always Sunny in Philadelphia": 84650830434,
"P:\\tv\\Star Wars - Skeleton Crew (2024)": 2940779001,
"P:\\tv\\Rupaul's Drag Race": 59664530796,
"P:\\tv\\Only Murders in the Building (2021)": 2379838148,
"P:\\tv\\Running Man": 10279755878,
"P:\\tv\\Shetland": 18537045340,
"P:\\tv\\Adults (2025)": 6845585714,
"P:\\tv\\iCarly (2021)": 19966043984,
"P:\\tv\\Villainous (2017)": 1961793524,
"P:\\tv\\The Terminal List - Dark Wolf (2025)": 9939046560,
"P:\\tv\\Ted Lasso (2020)": 52046307136,
"P:\\tv\\Murderbot (2025)": 18338040970,
"P:\\tv\\RuPaul's Drag Race Down Under": 27454793482,
"P:\\tv\\Gravity Falls": 31900305156,
"P:\\tv\\The Santa Clauses (2022)": 6400385164,
"P:\\tv\\Marvel's The Punisher (2017)": 32242478897,
"P:\\tv\\Dracula (2020)": 2147285239,
"P:\\tv\\Extraordinary": 6934203888,
"P:\\tv\\Cyberpunk - Edgerunners (2022)": 11313875182,
"P:\\tv\\Rick and Morty": 31672318625,
"P:\\tv\\Welcome to Chippendales (2022)": 10423545837,
"P:\\tv\\Squid Game (2021)": 22082475135,
"P:\\tv\\MobLand (2025)": 6622179548,
"P:\\tv\\Taskmaster (NZ)": 71323320898,
"P:\\tv\\The Newsroom": 27756667258,
"P:\\tv\\The Pretender": 18425629462,
"P:\\tv\\Hazbin Hotel (2024)": 10906489515,
"P:\\tv\\Raised by wolves": 9720677524,
"P:\\tv\\Tomb Raider - The Legend of Lara Croft": 9341088252,
"P:\\tv\\Spartacus": 75639017886,
"P:\\tv\\Worst Cooks in America (2010)": 27887501056,
"P:\\tv\\Avenue 5": 12572813494,
"P:\\tv\\Man Down (2013)": 5077144151,
"P:\\tv\\Outlander": 27364180668,
"P:\\tv\\The Eternaut": 17178505929,
"P:\\tv\\Below Deck Down Under (2022)": 36006759742,
"P:\\tv\\Dirty Laundry": 27626331672,
"P:\\tv\\Chilling Adventures of Sabrina (2018)": 23147355371,
"P:\\tv\\The Studio (2025)": 11530554023,
"P:\\tv\\The Forsytes (2025)": 4034792830,
"P:\\tv\\Platonic (2023)": 17488146510,
"P:\\tv\\Love Island (US) (2019)": 20699120877,
"P:\\tv\\Dark Side of the Ring": 11863132534,
"P:\\tv\\The Day of the Jackal (2024)": 17787097381,
"P:\\tv\\Utopia (AU)": 8691287022,
"P:\\tv\\Sweetpea": 2706241673,
"P:\\tv\\Dateline NBC (1992)": 19267231607,
"P:\\tv\\Euphoria": 40925172559,
"P:\\tv\\The Consultant (2023)": 74,
"P:\\tv\\Titans (2018)": 31986198137,
"P:\\tv\\Taskmaster": 142193364333,
"P:\\tv\\Ink Master": 23329086486,
"P:\\tv\\Dimension 20": 559394428110,
"P:\\tv\\Continuum (2012)": 29352883496,
"P:\\tv\\South Park": 70261225261,
"P:\\tv\\Letterkenny": 63,
"P:\\tv\\Ghosts (2019)": 40703143881,
"P:\\tv\\Moon Knight": 10976093361,
"P:\\tv\\Twisted Metal (2023)": 12547412897,
"P:\\tv\\Extrapolations": 6690715385,
"P:\\tv\\Quiet On Set - The Dark Side Of Kids TV": 12191520028,
"P:\\tv\\Sh\u014dgun": 20899988683,
"P:\\tv\\Taboo (2017)": 19309841226,
"P:\\tv\\Ironheart (2025)": 3153557870,
"P:\\tv\\DOTA - Dragon's Blood (2021)": 12538510766,
"P:\\tv\\Knuckles": 2140786440,
"P:\\tv\\Shoresy": 10192565178,
"P:\\tv\\Impractical Jokers": 13357380400,
"P:\\tv\\One More Time (2024)": 6434473461,
"P:\\tv\\Crowd Control": 9644641207,
"P:\\tv\\Dimension 20's Adventuring Party": 12002285238,
"P:\\tv\\Special Ops Lioness": 9765393961,
"P:\\tv\\Ted (2024)": 3024624414,
"P:\\tv\\Mighty Nein (2025)": 6138965943,
"P:\\tv\\Citadel - Diana": 13304679453,
"P:\\tv\\Our Flag Means Death": 2107045664,
"P:\\tv\\Make Some Noise": 26524123873,
"P:\\tv\\Mayor of Kingstown (2021)": 65464041666,
"P:\\tv\\The Take": 6020370013,
"P:\\tv\\Agatha All Along": 3411637969,
"P:\\tv\\The Amazing Digital Circus (2023)": 4739070191,
"P:\\tv\\The Now": 836886747,
"P:\\tv\\Poppa\u2019s House": 13794748297,
"P:\\tv\\Married at First Sight (2014)": 30275711911,
"P:\\tv\\The Closer": 47449608535,
"P:\\tv\\Junior Taskmaster (2024)": 4133620030,
"P:\\tv\\WondLa": 1399628000,
"P:\\tv\\The Second Best Hospital in the Galaxy (2024)": 3636394169,
"P:\\tv\\Being Human (2011)": 66311454464,
"P:\\tv\\SCORPION": 54081802764,
"P:\\tv\\The Goes Wrong Show (2019)": 3676343887,
"P:\\tv\\See": 12316511887,
"P:\\tv\\Dirk Gently's Holistic Detective Agency (2016)": 11935610182,
"P:\\tv\\Tokyo Override (2024)": 3802255332,
"P:\\tv\\Peacemaker (2022)": 13199970800,
"P:\\tv\\The Falcon and The Winter Soldier (2021)": 11657055937,
"P:\\tv\\Fargo (2014)": 93247402537,
"P:\\tv\\Killer Cakes": 3673781461,
"P:\\tv\\The Mandalorian": 36487773789,
"P:\\tv\\Very Important People": 14563355278,
"P:\\tv\\Smiling Friends": 5633340834,
"P:\\tv\\Game Changers (2024)": 5880504271,
"P:\\tv\\Star Strek Strange New Worlds": 13781151928,
"P:\\tv\\Galavant": 12147863291,
"P:\\tv\\She-Hulk Attorney at Law": 10233633417,
"P:\\tv\\From Dusk Till Dawn - The Series (2014)": 5360771338,
"P:\\tv\\The Journal of the Mysterious Creatures (2019)": 92,
"P:\\tv\\Fallen (2024)": 4161867429,
"P:\\tv\\Severance": 15044806873,
"P:\\tv\\The Great (2020)": 22361386693,
"P:\\tv\\What If": 21312022582,
"P:\\tv\\Rupaul's Drag Race UK": 110914388896,
"P:\\tv\\Game Of Thrones": 119681469870,
"P:\\tv\\Belgravia - The Next Chapter": 8340040939,
"P:\\tv\\Hitmen (2020)": 12274410846,
"P:\\tv\\Haunted Hotel (2025)": 4735071992,
"P:\\tv\\The Book of Boba Fett": 12039417291,
"P:\\tv\\SAS Rogue Heroes (2022)": 10733559643,
"P:\\tv\\Dwight in Shining Armor": 75,
"P:\\tv\\Jury Duty": 8010062372,
"P:\\tv\\Son of Zorn (2016)": 6780978712,
"P:\\tv\\The Gentlemen (2024)": 5224500371,
"P:\\tv\\Schmigadoon!": 6206632733,
"P:\\tv\\The Drew Carey Show (1995)": 70,
"P:\\tv\\Fired on Mars (2023)": 3590992124,
"P:\\tv\\Black Bird (2022)": 5893929480,
"P:\\tv\\Billions": 31141419259,
"P:\\tv\\Reacher (2022)": 17521873037,
"P:\\tv\\The Morning Show": 94311701751,
"P:\\tv\\Secret Level": 2810124465,
"P:\\tv\\The Boys": 68010010167,
"P:\\tv\\Gordon Ramsay's Food Stars (2023)": 6344621632,
"P:\\tv\\Death and Other Details": 17844763765,
"P:\\tv\\Modern Family": 82788065200,
"P:\\tv\\Married... with Children (1987)": 64228823786,
"P:\\tv\\BattleBots": 61,
"P:\\tv\\Silicon Valley (2014)": 63657428121,
"P:\\tv\\Tires (2024)": 5375794389,
"P:\\tv\\Creature Commandos (2024)": 2331424358,
"P:\\tv\\Goosebumps (2023)": 8257419062,
"P:\\tv\\The Fall of the House of Usher (2023)": 16454192941,
"P:\\tv\\Passion for punchlines": 75514795,
"P:\\tv\\The Queen's Gambit": 4100494817,
"P:\\tv\\Suits LA (2025)": 22274831381,
"P:\\tv\\Dune - Prophecy": 3330003290,
"P:\\tv\\Unstable": 5444623642,
"P:\\tv\\The Split": 7970767632,
"P:\\tv\\Barry": 31934844666,
"P:\\tv\\The Dragon Dentist": 11317084093,
"P:\\tv\\Kevin Can F-k Himself": 11614889793
} }

422
ARCHITECTURE.md Normal file
View File

@ -0,0 +1,422 @@
# Interactive Audio Stream Selection - Architecture Diagram
## System Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ main.py │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ ArgumentParser │ │
│ │ --filter-audio (enables audio filtering) │ │
│ │ --interactive (enables interactive mode) ← NEW │ │
│ │ --cq, --r, --m, --language, --test (existing) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ normalize_input_path() → folder path │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ process_folder( │ │
│ │ filter_audio=True/False, │ │
│ │ interactive_audio=True/False ← NEW │ │
│ │ ) │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ core/process_manager.py │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ process_folder(folder, ..., filter_audio, interactive) │ │
│ │ ↑ NEW param │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ For each video file: │ │
│ │ 1. Get source resolution & target resolution │ │
│ │ 2. Create audio_filter_config dict: │ │
│ │ { │ │
│ │ "enabled": filter_audio, │ │
│ │ "interactive": interactive_audio ← NEW FIELD │ │
│ │ } │ │
│ │ 3. Call run_ffmpeg() with audio_filter_config │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ core/encode_engine.py │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ run_ffmpeg( │ │
│ │ input_file, output_file, ..., │ │
│ │ audio_filter_config={enabled, interactive} │ │
│ │ ) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 1. streams = get_audio_streams(input_file) │ │
│ │ └─ Returns: [(index, ch, br, lang, meta), ...] │ │
│ │ │ │
│ │ 2. if audio_filter_config.get("enabled"): │ │
│ │ ├─ if audio_filter_config.get("interactive"): │ │
│ │ │ └─ Call: prompt_user_audio_selection(streams) ← ◆ │ │
│ │ │ [SHOWS PROMPT TO USER] │ │
│ │ │ └─ Returns: filtered_streams │ │
│ │ │ │ │
│ │ └─ else: │ │
│ │ └─ Call: filter_audio_streams(input_file, streams) │ │
│ │ (Automatic: keep best English + Commentary) │ │
│ │ └─ Returns: filtered_streams │ │
│ │ │ │
│ │ 3. For each stream in filtered_streams: │ │
│ │ ├─ choose_audio_bitrate() (codec selection) │ │
│ │ └─ Build FFmpeg codec params (-c:a, -b:a, etc.) │ │
│ │ │ │
│ │ 4. subprocess.run(ffmpeg_cmd) │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ core/audio_handler.py │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ def prompt_user_audio_selection(streams) ← NEW FUNCTION │ │
│ │ ◆ Interactive User Prompt ◆ │ │
│ │ │ │
│ │ Display: │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ 🎵 AUDIO STREAM SELECTION │ │ │
│ │ │ │ │ │
│ │ │ Stream #0: 2ch | Lang: eng | Bitrate: 128kbps │ │
│ │ │ Stream #1: 6ch | Lang: eng | Bitrate: 448kbps │ │
│ │ │ Stream #2: 2ch | Lang: spa | Bitrate: 128kbps │ │
│ │ │ Stream #3: 2ch | Lang: comment | Bitrate: 64kbps │ │
│ │ │ │ │ │
│ │ │ Keep streams: 1,3 │ │ │
│ │ │ │ │ │
│ │ │ ✅ Keeping 2 stream(s), removing 2 stream(s) │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Process: │ │
│ │ 1. Check if streams empty/single → return as-is │ │
│ │ 2. Display all streams with formatting │ │
│ │ 3. Prompt user for comma-separated indices │ │
│ │ 4. Parse and validate input │ │
│ │ 5. Filter streams to selected only │ │
│ │ 6. Log selections & removed streams │ │
│ │ 7. Return filtered_streams │ │
│ │ │ │
│ │ Error Handling: │ │
│ │ • Invalid input → Keep all (log warning) │ │
│ │ • No selections → Keep all (log warning) │ │
│ │ • Empty input → Keep all (user confirmed) │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
## Data Flow Example
### User Command
```bash
python main.py "C:\Videos" --filter-audio --interactive
```
### Data Transformation
```
Step 1: ArgumentParser
─────────────────────
Input Args:
folder = "C:\Videos"
filter_audio = True
interactive_audio = True
Output: args object
────────────────────────────────────────────────────────
Step 2: main() → process_folder()
───────────────────────────────────
Input:
folder, filter_audio=True, interactive_audio=True
Output: Called with both flags
────────────────────────────────────────────────────────
Step 3: process_folder() → Builds audio_filter_config
──────────────────────────────────────────────────────
Input:
filter_audio=True
interactive_audio=True
Logic:
if filter_audio is not None:
audio_filter_config = {
"enabled": True,
"interactive": True ← NEW
}
Output: audio_filter_config dict
────────────────────────────────────────────────────────
Step 4: process_folder() → run_ffmpeg()
─────────────────────────────────────────
Input:
input_file = "movie.mkv"
audio_filter_config = {"enabled": True, "interactive": True}
Output: Called with config
────────────────────────────────────────────────────────
Step 5: run_ffmpeg() → Audio Stream Detection
──────────────────────────────────────────────
Input:
input_file = "movie.mkv"
Output:
streams = [
(0, 2, 128, "eng", 0), # Stream #0: 2ch English 128kbps
(1, 6, 448, "eng", 0), # Stream #1: 6ch English 448kbps
(2, 2, 128, "spa", 0), # Stream #2: 2ch Spanish 128kbps
(3, 2, 64, "und", 0) # Stream #3: 2ch Undefined 64kbps
]
────────────────────────────────────────────────────────
Step 6: Audio Filtering Decision
────────────────────────────────
Input:
audio_filter_config = {"enabled": True, "interactive": True}
streams = [4 streams above]
Logic:
if audio_filter_config.get("enabled"): ✓ True
if audio_filter_config.get("interactive"): ✓ True
→ Call prompt_user_audio_selection() ← INTERACTIVE PATH
Output: User prompt shown to console
────────────────────────────────────────────────────────
Step 7: prompt_user_audio_selection() → User Input
──────────────────────────────────────────────────────
Input:
streams = [4 streams]
Display:
🎵 AUDIO STREAM SELECTION
════════════════════════════════════════════════════
Stream #0: 2ch | Lang: eng | Bitrate: 128kbps
Stream #1: 6ch | Lang: eng | Bitrate: 448kbps
Stream #2: 2ch | Lang: spa | Bitrate: 128kbps
Stream #3: 2ch | Lang: undefined | Bitrate: 64kbps
Keep streams: ← WAIT FOR USER INPUT
User Input:
"1,3"
Parse:
selected_indices = {1, 3}
Filter:
filtered = [
(1, 6, 448, "eng", 0), ✓ Keep
(3, 2, 64, "und", 0) ✓ Keep
]
Output:
✅ Keeping 2 stream(s), removing 2 stream(s)
Return: filtered streams
────────────────────────────────────────────────────────
Step 8: Back to run_ffmpeg() → Codec Selection
──────────────────────────────────────────────
Input:
streams = [
(1, 6, 448, "eng", 0),
(3, 2, 64, "und", 0)
]
Process each stream:
Stream 1: 6ch → choose_audio_bitrate() → ("eac3", 384000)
Stream 3: 2ch → choose_audio_bitrate() → ("aac", 160000)
Output:
FFmpeg codec params:
-c:a:1 eac3 -b:a:1 384k -ac:1 6 -channel_layout:1 5.1
-c:a:3 aac -b:a:3 160k -ac:3 2 -channel_layout:3 stereo
────────────────────────────────────────────────────────
Step 9: FFmpeg Encoding
───────────────────────
Input:
ffmpeg -i movie.mkv \
-vf scale=... \
-c:v av1_nvenc \
-c:a:1 eac3 -b:a:1 384k ... \
-c:a:3 aac -b:a:3 160k ... \
output.mkv
Process:
FFmpeg encodes video and audio streams
Only streams 1 and 3 included (streams 0 and 2 excluded)
Output:
output.mkv (with only selected audio tracks)
```
## State Diagram
```
┌─────────────────────────────────┐
│ User Runs Script │
│ --filter-audio --interactive │
└──────────────┬──────────────────┘
┌─────────────────────────────────┐
│ Parse Arguments │
│ interactive_audio = True │
└──────────────┬──────────────────┘
┌─────────────────────────────────┐
│ process_folder() │
│ Build audio_filter_config │
│ {enabled: T, interactive: T} │
└──────────────┬──────────────────┘
┌────────────┴────────────┐
│ │
▼ ▼
For each file Detect audio streams
┌──────────────┐ get_audio_streams()
│ run_ffmpeg() │ └─ Returns 4 streams
└──────┬───────┘
┌──────────────────────────┐
│ Check filter enabled? │
│ audio_filter_config │
└──────┬─────────────┬─────┘
│ No │ Yes
│ ▼
│ ┌─────────────────────┐
│ │ Check interactive? │
│ └────┬────────────┬───┘
│ │ No │ Yes
│ │ ▼
│ │ ┌───────────────────┐
│ │ │ INTERACTIVE PROMPT│
│ │ │ Show streams │
│ │ │ Get user input │
│ │ │ Filter streams │
│ │ └─────────┬─────────┘
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Automatic Filter │ │
│ │ (Best English + │ │
│ │ Commentary) │ │
│ └─────────┬────────┘ │
│ │ │
└────────────────┴───────────┘
┌────────────────────────────────┐
│ Apply Codec Selection │
│ (for selected streams only) │
│ choose_audio_bitrate() │
└────────────┬───────────────────┘
┌────────────────────────────────┐
│ Build FFmpeg Command │
│ (with selected audio streams) │
└────────────┬───────────────────┘
┌────────────────────────────────┐
│ Run FFmpeg Encoding │
│ subprocess.run(cmd) │
└────────────┬───────────────────┘
┌────────────────────────────────┐
│ Success/Failure Handling │
│ Log Results │
└────────────┬───────────────────┘
┌────────────┴─────────┐
│ │
Next file? Process Complete
```
## Component Interaction
```
┌─────────────┐
│ main.py │
└──────┬──────┘
│ calls with (filter_audio, interactive_audio)
┌──────────────────────┐
│ process_manager.py │
├──────────────────────┤
│ • Build config │ ◄─── Set "interactive" field
│ • For each file: │ in audio_filter_config
│ └─ run_ffmpeg() │
└──────┬───────────────┘
│ passes audio_filter_config
┌──────────────────────┐
│ encode_engine.py │
├──────────────────────┤
│ • Check "enabled" │ ◄─── Decide which
│ • Check "interactive"│ filtering method
│ • Route to: │ to use
│ ├─ interactive path│
│ └─ automatic path │
└──────┬───────────────┘
│ passes streams
┌──────────────────────┐
│ audio_handler.py │
├──────────────────────┤
│ • Interactive: │
│ prompt_user_...() │◄──── NEW FUNCTION
│ └─ Show & filter │ Shows prompt
│ │ Gets user input
│ • Automatic: │ Returns filtered
│ filter_audio_...() │
│ └─ Logic filter │
└──────────────────────┘
│ returns filtered streams
┌──────────────────────┐
│ encode_engine.py │
├──────────────────────┤
│ • Codec selection │
│ • Build FFmpeg cmd │
│ • Run encoding │
└──────────────────────┘
```
---
This architecture ensures clean separation of concerns:
- **main.py**: CLI interface
- **process_manager.py**: Orchestration & config building
- **encode_engine.py**: FFmpeg command building & execution
- **audio_handler.py**: Audio detection & stream filtering
The interactive prompt is cleanly isolated in `audio_handler.py` and only called when needed.

81
ENCODER_SWITCH.md Normal file
View File

@ -0,0 +1,81 @@
# Dual Encoder Support - Implementation Complete ✅
## Features Added
The transcoder now supports switching between two video encoders via the `--encoder` CLI option:
### 1. **HEVC NVENC 10-bit** (Default)
- **Command**: `--encoder nvenc` or default (no flag needed)
- **Codec**: `hevc_nvenc`
- **Preset**: `slow` (high quality)
- **Bit Depth**: 10-bit
- **Pixel Format**: `yuv420p10le`
- **Use Case**: Best quality archival format, suitable for Plex compatibility
### 2. **AV1 NVENC 8-bit**
- **Command**: `--encoder av1`
- **Codec**: `av1_nvenc`
- **Preset**: `p7` (high quality)
- **Bit Depth**: 8-bit
- **Pixel Format**: `yuv420p`
- **Use Case**: Maximum file size reduction, modern playback devices
## Usage Examples
```bash
# Default to HEVC NVENC 10-bit with smart resolution scaling
python main.py "C:\Videos\Movies"
# Force AV1 NVENC 8-bit encoding
python main.py "C:\Videos\TV" --encoder av1
# AV1 with explicit resolution
python main.py "C:\Videos\Anime" --encoder av1 --r 1080
# AV1 with CQ mode at specific quality
python main.py "C:\Videos\Low-Res" --encoder av1 --cq 28
# AV1 with bitrate mode
python main.py "C:\Videos\Movies" --encoder av1 --m bitrate
# HEVC (explicit, though it's the default)
python main.py "C:\Videos\TV" --encoder nvenc --cq 26
```
## Configuration
Encoder settings are stored in `config.xml`:
```xml
<encoder default="nvenc">
<av1_nvenc preset="p7" bit_depth="8" pix_fmt="yuv420p" />
<hevc_nvenc preset="slow" bit_depth="10" pix_fmt="yuv420p10le" />
</encoder>
```
The `default="nvenc"` attribute can be changed, but CLI `--encoder` flag always takes precedence.
## Files Modified
1. **config.xml** - Added `<encoder>` section with both encoder configurations
2. **main.py** - Added `--encoder` argument, defaults to "nvenc"
3. **encode_engine.py** - Updated `run_ffmpeg()` to:
- Accept `encoder` parameter
- Dynamically set encoder codec, preset, bit depth, and pixel format
- Display encoder details in logging output
4. **process_manager.py** - Updated to:
- Accept and pass `encoder` parameter through processing pipeline
- Updated both Phase 1 (initial encode) and Phase 2 (bitrate retry) encode calls
## Quality Notes
| Aspect | HEVC NVENC | AV1 NVENC |
|--------|-----------|----------|
| **File Size** | ~80-90% of AV1 | Smallest (baseline) |
| **Quality** | Excellent | Excellent |
| **Preset** | slow (p6) | p7 |
| **Bit Depth** | 10-bit | 8-bit |
| **Compatibility** | Excellent (Plex) | Good (modern devices) |
| **Encoding Speed** | Fast | Fast |
Both encoders use NVIDIA GPU acceleration (NVENC) for fast encoding.

280
IMPLEMENTATION_COMPLETE.md Normal file
View File

@ -0,0 +1,280 @@
# Interactive Audio Stream Selection - Complete Implementation
## Overview
**COMPLETE** - Interactive audio stream selection feature has been successfully implemented.
Users can now view all available audio streams in each video file and select which ones to keep for encoding, providing fine-grained control over audio track inclusion.
## Features Implemented
### 1. Stream Display ✅
- Shows all audio streams with human-readable format
- Displays: Stream number, channel count, language code, bitrate
- Clear visual separation and organized layout
- Example: `Stream #0: 2ch | Lang: eng | Bitrate: 128kbps`
### 2. User Input ✅
- Accepts comma-separated stream indices: `0,1,3`
- Accepts single stream: `1`
- Accepts blank input (keep all streams)
- Input validation with helpful error messages
- Optional spaces in comma-separated list: `0, 1, 3`
### 3. Filtering ✅
- Removes non-selected streams from encoding
- Preserves original stream indices for FFmpeg mapping
- Logs all selections and removals
- Falls back to keeping all streams on invalid input
### 4. CLI Integration ✅
- New flag: `--interactive` (boolean)
- Works with `--filter-audio` flag
- Can be used independently (auto-enables filtering)
- Integrated into argument parser with help text
### 5. Processing Pipeline ✅
- Called from `run_ffmpeg()` in encode_engine.py
- Executed after stream detection
- Executed before codec selection
- Per-file prompting (allows different selections per video)
### 6. Logging ✅
- Logs user selections: `User selected X audio stream(s): [0, 1, 3]`
- Logs removed streams: `Removed X audio stream(s): [2]`
- Logs invalid input attempts
- Integrated with project's logging system
## File Changes Summary
### main.py
**Added**:
- `--interactive` argument to argparse
- Pass `args.interactive_audio` to `process_folder()`
**Lines Changed**: 2
### core/process_manager.py
**Added**:
- `interactive_audio: bool = False` parameter to function signature
- Logic to set `audio_filter_config["interactive"]` based on CLI args
- Auto-enable filtering if `--interactive` used without `--filter-audio`
**Lines Changed**: ~5
### core/encode_engine.py
**Added**:
- Import `prompt_user_audio_selection`
- Check for `audio_filter_config.get("interactive", False)`
- Route to interactive or automatic filtering accordingly
**Lines Changed**: ~5
### core/audio_handler.py
**Added**:
- `prompt_user_audio_selection()` function (64 lines)
- Comprehensive docstring
- User-friendly output formatting
- Input validation and error handling
- Logging integration
**Lines Changed**: +64 (new function)
## Code Structure
### Function: `prompt_user_audio_selection(streams: list) -> list`
**Location**: `core/audio_handler.py` (line 297)
**Parameters**:
- `streams`: List of (index, channels, bitrate, language, metadata) tuples
**Returns**:
- Filtered list containing only user-selected streams
**Key Features**:
1. Early return if 0-1 streams (no selection needed)
2. Display header with visual formatting
3. Show each stream with index, channels, language, bitrate
4. Prompt for user input with examples
5. Parse comma-separated input
6. Validate stream indices
7. Handle edge cases (empty input, invalid input)
8. Log results to project logger
9. Return filtered streams ready for encoding
**Error Handling**:
- ValueError on unparseable input → keep all
- No valid selections → keep all with warning
- Empty input → keep all (user confirmed)
## Execution Flow
```
User runs:
$ python main.py "C:\Videos" --filter-audio --interactive
main.py parses arguments
- filter_audio = True (from --filter-audio)
- interactive_audio = True (from --interactive)
process_folder() called with both flags
For each video file:
└─ run_ffmpeg() called
└─ get_audio_streams() detects streams
└─ Check audio_filter_config.enabled
└─ True: Apply filtering
└─ Check audio_filter_config.interactive
└─ True: Call prompt_user_audio_selection()
└─ [INTERACTIVE PROMPT APPEARS]
└─ User sees streams and selects
└─ Returns filtered stream list
└─ False: Call filter_audio_streams()
└─ Automatic filtering (keep best English + Commentary)
└─ Process selected streams for encoding
```
## Usage Examples
### Basic Interactive Mode
```bash
python main.py "C:\Videos\Movies" --filter-audio --interactive
```
### Combined with Other Options
```bash
python main.py "C:\Videos\TV" --filter-audio --interactive --cq 28 --r 1080 --language eng
```
### Interactive Without Explicit --filter-audio
```bash
python main.py "C:\Videos\Anime" --interactive
```
(Filtering is auto-enabled with interactive mode)
## Testing Scenarios
### Scenario 1: Multiple Audio Languages
**Input**: Video with English (stereo), English (5.1), Spanish, Commentary
**Expected**: Prompt shows 4 streams, user can select any combination
### Scenario 2: Invalid Selection
**Input**: User types "abc" or non-existent stream number
**Expected**: Tool logs warning, keeps all streams, continues
### Scenario 3: Single Audio Stream
**Input**: Video with only one audio track
**Expected**: Function returns early, no prompt shown
### Scenario 4: Empty Input
**Input**: User presses Enter without typing
**Expected**: All streams kept, confirmation message shown
## Backward Compatibility
✅ **Fully Backward Compatible**
- Existing `--filter-audio` behavior unchanged
- New feature is opt-in via `--interactive` flag
- Default behavior (no interactive) preserved
- No changes to config.xml schema required
- All existing scripts/automation continues to work
## Integration Points
### With Audio Language Tagging
- `--language eng --filter-audio --interactive` works together
- User selects streams, then language metadata applied to all
### With Resolution/CQ Options
- `--filter-audio --interactive --cq 28 --r 1080` fully compatible
- Interactive selection happens first, encoding follows
### With Test Mode
- `--filter-audio --interactive --test` shows interactive prompt on first file
- Useful for testing selections before batch encoding
## Performance Impact
✅ **Minimal Impact**
- Interactive prompt only appears when user explicitly requests it
- No performance overhead when `--interactive` not used
- Per-file prompt adds negligible time (user wait for input)
- No change to FFmpeg encoding performance
## Documentation Provided
1. **INTERACTIVE_AUDIO.md** - User guide with examples
2. **IMPLEMENTATION_NOTES.md** - Technical implementation details
3. **QUICK_REFERENCE.md** - Quick reference guide and FAQ
4. This summary document
## Completion Checklist
✅ Function implementation (prompt_user_audio_selection)
✅ CLI argument (--interactive)
✅ Integration with process_manager
✅ Integration with encode_engine
✅ Input validation
✅ Error handling
✅ Logging integration
✅ Backward compatibility
✅ Documentation
✅ Syntax validation
✅ Code review
## Example Output
When user runs with `--filter-audio --interactive`:
```
================================================================================
🎵 AUDIO STREAM SELECTION
================================================================================
Stream #0: 2ch | Lang: eng | Bitrate: 128kbps
Stream #1: 6ch | Lang: eng | Bitrate: 448kbps
Stream #2: 2ch | Lang: spa | Bitrate: 128kbps
Stream #3: 2ch | Lang: comment | Bitrate: 64kbps
────────────────────────────────────────────────────────────────────────────
Enter stream numbers to keep (comma-separated, e.g.: 1,2 or just 2)
Leave blank to keep all streams
────────────────────────────────────────────────────────────────────────────
➜ Keep streams: 1,3
✅ Keeping 2 stream(s), removing 2 stream(s)
🎬 Running CQ encode: output.mkv
...
```
## Next Steps (Optional Enhancements)
Future improvements could include:
- [ ] Preset buttons for common selections (e.g., "Best Audio", "English Only", "All")
- [ ] Auto-numbering display for clarity
- [ ] Arrow key selection interface (more interactive)
- [ ] Save/load selection templates for batch consistency
- [ ] GUI interface for stream selection
- [ ] Default selection from config for silent/batch operation
---
## Summary
The interactive audio stream selection feature is **complete and ready for use**. Users can now:
1. ✅ See all available audio streams with details
2. ✅ Choose which streams to keep for encoding
3. ✅ Get immediate confirmation of their selection
4. ✅ Have per-file control in batch operations
5. ✅ Maintain automatic fallback if input is invalid
The implementation is clean, well-documented, backward-compatible, and fully integrated into the existing codebase.

141
IMPLEMENTATION_NOTES.md Normal file
View File

@ -0,0 +1,141 @@
# Interactive Audio Stream Selection - Implementation Summary
## Changes Made
### 1. New Function: `prompt_user_audio_selection()` in audio_handler.py
- **Purpose**: Display audio streams and prompt user for selection
- **Input**: List of streams with (index, channels, bitrate, language, metadata)
- **Output**: Filtered list containing only user-selected streams
- **Features**:
- Displays stream info: `Stream #X: YYch | Lang: YYY | Bitrate: XYZkbps`
- Accepts comma-separated input: `1,2,3` or `1` or empty (keep all)
- Validates input and logs selections
- Falls back to keeping all streams on invalid input
### 2. Updated: `run_ffmpeg()` in encode_engine.py
- Now checks `audio_filter_config.get("interactive", False)`
- Routes to interactive prompt if `interactive=True`
- Routes to automatic filtering if `interactive=False`
- Both modes filter streams before codec selection
### 3. Updated: `process_folder()` in process_manager.py
- New parameter: `interactive_audio: bool = False`
- Builds audio_filter_config with both `enabled` and `interactive` fields
- If `--interactive` used without `--filter-audio`, enables both automatically
### 4. Updated: main.py
- New CLI argument: `--interactive`
- Action: `store_true` (binary flag)
- Passed through to `process_folder()`
- Help text: "Interactive mode: show audio streams and let user select which to keep (requires --filter-audio)"
## Usage Examples
### Example 1: Automatic Filtering (Existing)
```bash
python main.py "C:\Videos" --filter-audio
```
- Automatically keeps best English + Commentary
- No user interaction
### Example 2: Interactive Selection (New)
```bash
python main.py "C:\Videos" --filter-audio --interactive
```
- Shows each file's audio streams
- User picks which streams to keep
- Different selections per file allowed
### Example 3: Interactive Without --filter-audio
```bash
python main.py "C:\Videos" --interactive
```
- Same as Example 2 (enables filtering automatically)
- More intuitive UX
## Stream Display Format
When interactive mode runs, user sees:
```
================================================================================
🎵 AUDIO STREAM SELECTION
================================================================================
Stream #0: 2ch | Lang: eng | Bitrate: 128kbps
Stream #1: 6ch | Lang: eng | Bitrate: 448kbps
Stream #2: 2ch | Lang: spa | Bitrate: 128kbps
────────────────────────────────────────────────────────────────────────────
Enter stream numbers to keep (comma-separated, e.g.: 1,2 or just 2)
Leave blank to keep all streams
────────────────────────────────────────────────────────────────────────────
➜ Keep streams:
```
## Logging Output
When user selects streams:
```
✅ Keeping 2 stream(s), removing 1 stream(s)
User selected 2 audio stream(s): [1, 2]
Removed 1 audio stream(s): [0]
```
## Audio Filter Config Structure
**Old (Automatic only)**:
```python
{
"enabled": True/False
}
```
**New (With Interactive)**:
```python
{
"enabled": True/False,
"interactive": True/False
}
```
## Flow Diagram
```
main.py
└─ parse args (--filter-audio, --interactive)
└─ process_folder()
└─ for each file:
└─ run_ffmpeg()
└─ get_audio_streams()
└─ if audio_filter_config.enabled:
├─ if audio_filter_config.interactive:
│ └─ prompt_user_audio_selection() ← NEW
│ └─ [User sees streams and selects]
└─ else:
└─ filter_audio_streams() (automatic)
└─ encode with selected streams
```
## Input Validation
- **Valid**: `1`, `0,1,3`, `2, 3, 5` (spaces OK)
- **Invalid**: `abc`, `1.5`, `1-3` (ranges not supported)
- **On Invalid**: Keep all streams, log warning
## Edge Cases Handled
1. **No streams**: Return original (nothing to filter)
2. **Single stream**: Return as-is (no selection needed)
3. **Invalid stream indices**: Keep all streams
4. **Empty input**: Keep all streams
5. **No valid selections**: Keep all streams (with warning)
## Backward Compatibility
- Existing `--filter-audio` behavior unchanged (automatic mode)
- `--interactive` is optional, defaults to False
- No breaking changes to config.xml structure
- Language tagging (--language) still works alongside audio filtering

109
INTERACTIVE_AUDIO.md Normal file
View File

@ -0,0 +1,109 @@
# Interactive Audio Stream Selection
## Overview
The conversion tool now supports **interactive audio stream selection**, allowing you to manually choose which audio tracks to keep during encoding rather than relying on automatic filtering.
## Usage
### Enable Interactive Mode
Use both `--filter-audio` and `--interactive` flags together:
```bash
python main.py "C:\path\to\videos" --filter-audio --interactive
```
### What Happens
When encoding each file with multiple audio streams:
1. **Audio Stream Display**
- The tool displays all available audio streams with details:
```
🎵 AUDIO STREAM SELECTION
================================================================================
Stream #0: 2ch | Lang: eng | Bitrate: 128kbps
Stream #1: 6ch | Lang: eng | Bitrate: 448kbps
Stream #2: 2ch | Lang: spa | Bitrate: 128kbps
Stream #3: 2ch | Lang: comment | Bitrate: 64kbps
```
2. **User Prompt**
- You're asked to select which streams to keep:
```
────────────────────────────────────────────────────────────────────────────
Enter stream numbers to keep (comma-separated, e.g.: 1,2 or just 2)
Leave blank to keep all streams
────────────────────────────────────────────────────────────────────────────
➜ Keep streams: 1,3
```
3. **Encoding
**
- Only selected streams are included in the encoded output
- Other streams are removed
- Selection is logged for reference
## Input Format
- **Multiple streams**: `0,1,3` or `0, 1, 3` (spaces optional)
- **Single stream**: `1` or `2`
- **Keep all**: Press Enter without typing anything
## Example Scenarios
### Scenario 1: Keep Main Audio Only
```
Streams:
Stream #0: 2ch (English, 128kbps)
Stream #1: 6ch (English Surround, 448kbps) ← Best quality
Stream #2: 2ch (Spanish, 128kbps)
Input: 1
Result: Only Stream #1 (6ch English Surround) is encoded
```
### Scenario 2: Keep Multiple Languages
```
Streams:
Stream #0: 2ch (English, 128kbps)
Stream #1: 6ch (English Surround, 448kbps)
Stream #2: 2ch (Spanish, 128kbps)
Stream #3: 2ch (Commentary, 64kbps)
Input: 1,2,3
Result: Streams #1, #2, and #3 are encoded (English Surround, Spanish, Commentary)
```
## Comparison with Automatic Filtering
### Automatic Mode (--filter-audio only)
- Keeps: Best English audio + all Commentary tracks
- No user interaction
- Faster batch processing
### Interactive Mode (--filter-audio --interactive)
- Shows all streams and asks user to choose
- Per-file control
- Better for selective archiving/organization
- Useful when automatic filtering doesn't match your preferences
## Logging
All user selections are logged to the conversion log for reference:
```
User selected 2 audio stream(s): [1, 3]
Removed 1 audio stream(s): [2]
```
## Notes
- Interactive mode requires `--filter-audio` to be enabled
- If you use `--interactive` without `--filter-audio`, filtering is automatically enabled
- Invalid input (non-existent stream numbers) falls back to keeping all streams
- Empty input keeps all audio streams unchanged
- The prompt appears for each file being encoded, allowing different selections per file

View File

@ -1,7 +1,7 @@
# AV1 Batch Video Transcoder - Project Structure # AV1 Batch Video Transcoder - Project Structure
## Overview ## Overview
A modular batch AV1 video transcoding system using NVIDIA's av1_nvenc codec (10-bit p010le) with intelligent audio/video processing, subtitle embedding, and optional audio language tagging. A modular batch AV1 video transcoding system using NVIDIA's av1_nvenc codec (8-bit yuv420p) with intelligent audio/video processing, subtitle embedding, and optional audio language tagging.
## Recent Changes (Latest Session) ## Recent Changes (Latest Session)
@ -64,7 +64,7 @@ A modular batch AV1 video transcoding system using NVIDIA's av1_nvenc codec (10-
#### `core/encode_engine.py` #### `core/encode_engine.py`
- **`run_ffmpeg(input_file, output_file, cq, scale_width, scale_height, src_width, src_height, filter_flags, audio_config, method, bitrate_config, subtitle_file, audio_language)`** - **`run_ffmpeg(input_file, output_file, cq, scale_width, scale_height, src_width, src_height, filter_flags, audio_config, method, bitrate_config, subtitle_file, audio_language)`**
- Builds FFmpeg command with av1_nvenc codec (preset p1, pix_fmt p010le) - Builds FFmpeg command with av1_nvenc codec (preset p7, pix_fmt yuv420p)
- Per-stream audio codec/bitrate decisions - Per-stream audio codec/bitrate decisions
- Conditional subtitle input mapping (if subtitle_file provided) - Conditional subtitle input mapping (if subtitle_file provided)
- Optional audio language metadata (only if audio_language not None) - Optional audio language metadata (only if audio_language not None)

182
QUICK_REFERENCE.md Normal file
View File

@ -0,0 +1,182 @@
# Interactive Audio Selection - Quick Reference
## Command Syntax
### Enable Interactive Audio Selection
```bash
python main.py "C:\path\to\videos" --filter-audio --interactive
```
### Other Flags (Optional)
```bash
--filter-audio --interactive --cq 28 --r 1080 --language eng --test
```
## What User Sees
### Per File Prompt (appears for each video)
```
================================================================================
🎵 AUDIO STREAM SELECTION
================================================================================
Stream #0: 2ch | Lang: eng | Bitrate: 128kbps
Stream #1: 6ch | Lang: eng | Bitrate: 448kbps
Stream #2: 2ch | Lang: spa | Bitrate: 128kbps
Stream #3: 2ch | Lang: comment | Bitrate: 64kbps
────────────────────────────────────────────────────────────────────────────
Enter stream numbers to keep (comma-separated, e.g.: 1,2 or just 2)
Leave blank to keep all streams
────────────────────────────────────────────────────────────────────────────
➜ Keep streams:
```
### User Input Examples
| Input | Result |
|-------|--------|
| `1` | Keep Stream #1 (6ch English, 448kbps) |
| `1,3` | Keep Streams #1 and #3 |
| `0,1,2` | Keep Streams #0, #1, and #2 |
| ` ` (blank) | Keep all 4 streams |
### Expected Output
```
✅ Keeping 1 stream(s), removing 3 stream(s)
🎬 Running CQ encode: output.mkv
...
```
## Features
**Per-File Control**: Different selections for each video
**Clear Display**: See channel count, language, bitrate for each stream
**Flexible Input**: Comma-separated numbers, optional spaces
**Safe Defaults**: Invalid input keeps all streams
**Logging**: All selections recorded in conversion log
**Backwards Compatible**: Doesn't break existing workflows
## Common Scenarios
### Movie with Multiple Audio Tracks
```
Stream #0: 2ch English (128kbps)
Stream #1: 6ch English Surround (448kbps) ← Main audio
Stream #2: 2ch Spanish (128kbps)
Stream #3: 2ch Commentary (64kbps)
Input: 1,3
Output: Keep English 5.1 + Commentary
```
### TV Episode with Multiple Languages
```
Stream #0: 6ch English (384kbps)
Stream #1: 6ch Spanish (384kbps)
Stream #2: 2ch Commentary (64kbps)
Input: 0,1,2
Output: Keep all (English, Spanish, Commentary)
```
### File with Only One Audio Track
```
Stream #0: 6ch English (448kbps)
Input: (blank or 0)
Output: Keep the only track
```
## FAQ
**Q: What if I provide an invalid stream number?**
A: The tool keeps all streams and logs a warning.
**Q: Can I specify stream ranges like "0-2"?**
A: No, use comma-separated individual numbers: "0,1,2"
**Q: Do I have to answer the prompt for every file?**
A: Yes, this allows different selections per file. Use automatic --filter-audio mode if you want consistent filtering across all files.
**Q: What happens with invalid input like "abc" or "1.5"?**
A: The tool keeps all streams and logs the invalid input. Then continues to the next file.
**Q: Does --interactive work alone?**
A: Yes! If you use --interactive without --filter-audio, filtering is automatically enabled with interactive mode.
**Q: Can I combine this with --language tagging?**
A: Yes! Use: `--filter-audio --interactive --language eng`
This lets you select streams AND tag them with language metadata.
## Integration Points
### When Called
- After audio stream detection in `run_ffmpeg()`
- Before codec selection and FFmpeg command building
- Only if `audio_filter_config.enabled = True` AND `audio_filter_config.interactive = True`
### Stream Information Provided
- **Index**: Stream number in FFmpeg (0-based)
- **Channels**: 2ch, 6ch, etc.
- **Language**: eng, spa, und (undefined), etc.
- **Bitrate**: Detected bitrate in kbps
### What Gets Removed
- All streams NOT selected by user
- Metadata and descriptors for removed streams
- No re-encoding of audio (codec decisions apply per stream)
## Tips & Tricks
### Keeping Only Surround Audio
Most videos have stereo + surround. To keep only 5.1/6ch:
```
Input: 1 (if Stream #1 is 6ch)
```
### Keeping All Commentary
Commentary tracks are usually indexed separately:
```
Input: 0,2,3 (Stream #0 main + #2 and #3 commentary)
```
### English Only
If you have multiple languages:
```
Input: 0,1 (Stream #0 and #1 English only)
```
## Log Output Examples
**Successful Selection**
```
User selected 2 audio stream(s): [1, 3]
Removed 1 audio stream(s): [0, 2]
```
**Invalid Input**
```
User provided invalid audio selection input
Keeping all audio streams
```
**No Selection**
```
Keeping all audio streams
```
## Troubleshooting
**Issue**: Prompt doesn't appear
- **Solution**: Make sure both --filter-audio AND --interactive are specified
**Issue**: Selection is ignored
- **Solution**: Check log file for errors. Verify stream indices exist.
**Issue**: Want automatic mode back
- **Solution**: Use --filter-audio alone (without --interactive)

View File

@ -4,8 +4,9 @@ A high-performance batch video transcoding tool using NVIDIA's **AV1 NVENC** cod
## ✨ Key Features ## ✨ Key Features
- **10-bit AV1 Encoding** - NVIDIA GPU acceleration (p010le, preset p1) - **8-bit AV1 Encoding** - NVIDIA GPU acceleration (yuv420p, preset p7)
- **Smart Audio Processing** - Auto-detects bitrate, downmixes, re-encodes only when needed - **Smart Audio Processing** - Auto-detects bitrate, AAC for stereo, EAC3 for 5.1, downmixes, re-encodes only when needed
- **Audio Filtering** - Keep only best English audio + Commentary tracks (remove other languages)
- **Subtitle Embedding** - Auto-detects and embeds subtitles (.vtt, .srt, .ass, .ssa, .sub) - **Subtitle Embedding** - Auto-detects and embeds subtitles (.vtt, .srt, .ass, .ssa, .sub)
- **Smart Resolution** - Scales 4K→1080p, preserves lower resolutions - **Smart Resolution** - Scales 4K→1080p, preserves lower resolutions
- **Two-Phase Encoding** - CQ mode first, automatic Bitrate fallback if size threshold exceeded - **Two-Phase Encoding** - CQ mode first, automatic Bitrate fallback if size threshold exceeded
@ -113,7 +114,7 @@ Show.S01E01 - [EHX].mkv (450MB, subtitle embedded, audio tagged)
| Setting | Value | | Setting | Value |
|---------|-------| |---------|-------|
| Video Codec | AV1 (av1_nvenc) | | Video Codec | AV1 (av1_nvenc) |
| Bit Depth | 10-bit (p010le) | | Bit Depth | 8-bit (yuv420p) |
| GPU Preset | p1 (high quality) | | GPU Preset | p1 (high quality) |
| Audio Codec | AAC | | Audio Codec | AAC |
| Audio Mode | Smart (copy or re-encode) | | Audio Mode | Smart (copy or re-encode) |

View File

@ -56,7 +56,7 @@ python main.py "P:\tv\Show" --language eng
## Features ## Features
- **Hardware Encoding**: NVIDIA av1_nvenc (10-bit p010le, preset p1) - **Hardware Encoding**: NVIDIA av1_nvenc (8-bit yuv420p, preset p7)
- **Smart Audio**: Analyzes streams, re-encodes excessive bitrate, preserves good quality - **Smart Audio**: Analyzes streams, re-encodes excessive bitrate, preserves good quality
- **Smart Video**: Detects source resolution, scales 4K→1080p, preserves lower resolutions - **Smart Video**: Detects source resolution, scales 4K→1080p, preserves lower resolutions
- **Subtitle Detection**: Auto-finds and embeds subtitles (vtt, srt, ass, ssa, sub) - **Subtitle Detection**: Auto-finds and embeds subtitles (vtt, srt, ass, ssa, sub)
@ -83,7 +83,7 @@ Edit `config.xml` to customize:
1. **Detect subtitles**: Looks for matching `.en.vtt`, `.srt`, etc. 1. **Detect subtitles**: Looks for matching `.en.vtt`, `.srt`, etc.
2. **Analyze source**: Resolution, audio streams, bitrates 2. **Analyze source**: Resolution, audio streams, bitrates
3. **FFmpeg encode**: 3. **FFmpeg encode**:
- Video: AV1 NVENC (10-bit p010le) - Video: AV1 NVENC (8-bit yuv420p)
- Audio: Per-stream decisions (copy or re-encode) - Audio: Per-stream decisions (copy or re-encode)
- Subtitles: Embedded as SRT (if found) - Subtitles: Embedded as SRT (if found)
4. **Size check**: Compare output vs original (default 75% threshold) 4. **Size check**: Compare output vs original (default 75% threshold)

View File

@ -15,10 +15,10 @@
<extensions>.mkv,.mp4</extensions> <extensions>.mkv,.mp4</extensions>
<!-- File name tags to skip/ignore --> <!-- File name tags to skip/ignore -->
<ignore_tags>ehx</ignore_tags> <!-- ,megusta --> <ignore_tags>ehx,._</ignore_tags> <!-- ehx = encoded tag, ._ = macOS metadata files -->
<!-- Reduction ratio threshold: output must be <= this % of original or encoding fails --> <!-- Reduction ratio threshold: output must be <= this % of original or encoding fails -->
<reduction_ratio_threshold>0.75</reduction_ratio_threshold> <reduction_ratio_threshold>0.85</reduction_ratio_threshold>
<!-- Subtitle settings --> <!-- Subtitle settings -->
<subtitles> <subtitles>
@ -27,6 +27,13 @@
<codec>srt</codec> <codec>srt</codec>
</subtitles> </subtitles>
<!-- Audio track filtering: keep only best English audio + Commentary -->
<audio_filter>
<enabled>false</enabled>
<!-- When true: keeps primary English audio (most channels/bitrate) + any Commentary tracks -->
<!-- When false: keeps all audio tracks -->
</audio_filter>
<!-- Audio language tag --> <!-- Audio language tag -->
<audio_language>eng</audio_language> <audio_language>eng</audio_language>
</general> </general>
@ -44,12 +51,24 @@
ENCODE SETTINGS ENCODE SETTINGS
============================= --> ============================= -->
<encode> <encode>
<!-- CQ defaults (per resolution / content type) --> <!-- CQ defaults (per resolution / content type / encoder) -->
<cq> <cq>
<tv_1080>30</tv_1080> <av1>
<tv_1080>32</tv_1080>
<tv_720>34</tv_720> <tv_720>34</tv_720>
<anime_1080>32</anime_1080>
<anime_720>34</anime_720>
<movie_1080>32</movie_1080> <movie_1080>32</movie_1080>
<movie_720>34</movie_720> <movie_720>34</movie_720>
</av1>
<hevc>
<tv_1080>28</tv_1080>
<tv_720>30</tv_720>
<anime_1080>28</anime_1080>
<anime_720>30</anime_720>
<movie_1080>28</movie_1080>
<movie_720>30</movie_720>
</hevc>
</cq> </cq>
<!-- Fallback bitrate-based mode --> <!-- Fallback bitrate-based mode -->

View File

@ -504,3 +504,104 @@ tv,Supernatural,Supernatural - S14E13 - Lebanon x265 AC3 Bluray-1080p HiQVE - [E
tv,Supernatural,Supernatural - S14E14 - Ouroboros x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1085.86,376.39,34.7,1920x1080,1920x1080,1,34,CQ tv,Supernatural,Supernatural - S14E14 - Ouroboros x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1085.86,376.39,34.7,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S14E15 - Peace of Mind x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1209.11,476.01,39.4,1920x1080,1920x1080,1,34,CQ tv,Supernatural,Supernatural - S14E15 - Peace of Mind x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1209.11,476.01,39.4,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S14E16 - Don't Go in the Woods x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,998.67,353.21,35.4,1920x1080,1920x1080,1,34,CQ tv,Supernatural,Supernatural - S14E16 - Don't Go in the Woods x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,998.67,353.21,35.4,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S14E17 - Game Night x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1171.18,478.27,40.8,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S14E18 - Absence x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1149.74,496.79,43.2,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S14E19 - Jack in the Box x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1119.57,345.82,30.9,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S14E20 - Moriah x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1194.42,467.3,39.1,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,"Supernatural - S11E01 - Out of the Darkness, Into the Fire x265 AC3 Bluray-1080p HiQVE - [EHX].mkv",1081.05,491.08,45.4,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S11E02 - Form and Void x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1150.4,441.72,38.4,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S11E03 - The Bad Seed x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1000.66,354.67,35.4,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S11E04 - Baby x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1221.59,650.84,53.3,1920x1080,1920x1080,2,34,CQ
tv,Supernatural,Supernatural - S11E05 - Thin Lizzie x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1111.38,412.77,37.1,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S11E06 - Our Little World x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1121.15,399.25,35.6,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S11E07 - Plush x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1115.48,444.02,39.8,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S11E08 - Just My Imagination x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1003.32,400.98,40.0,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S11E09 - O Brother Where Art Thou x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1056.77,442.89,41.9,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S11E10 - The Devil in the Details x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1057.23,456.3,43.2,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S11E11 - Into the Mystic x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1115.26,457.89,41.1,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S11E12 - Dont You Forget about Me x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1054.6,449.94,42.7,1920x1080,1920x1080,1,34,CQ
tv,Supernatural,Supernatural - S11E13 - Love Hurts x265 AC3 Bluray-1080p HiQVE - [EHX].mkv,1017.88,371.56,36.5,1920x1080,1920x1080,1,34,CQ
tv,Extrapolations,Extrapolations.S01E08.2070.Ecocide.1080p.ATVP.WEB-DL.DDP5.1.H.264-EniaHD - [EHX].mkv,4485.35,821.95,18.3,1920x872,1920x872,2,30,CQ
tv,Extrapolations,Extrapolations.S01E07.2068.The.Going-Away.Party.1080p.ATVP.WEB-DL.DDP5.1.H.264-EniaHD - [EHX].mkv,3977.53,517.86,13.0,1920x872,1920x872,2,30,CQ
tv,Extrapolations,Extrapolations.S01E04.2059.Face.of.God.1080p.ATVP.WEB-DL.DDP5.1.H.264-EniaHD - [EHX].mkv,4348.62,655.87,15.1,1920x872,1920x872,2,30,CQ
tv,Extrapolations,Extrapolations.S01E05.2059.Part.II.Nightbirds.1080p.ATVP.WEB-DL.DDP5.1.H.264-EniaHD - [EHX].mkv,4358.04,875.13,20.1,1920x872,1920x872,2,30,CQ
tv,Extrapolations,Extrapolations.S01E02.2046.Whale.Fall.1080p.ATVP.WEB-DL.DDP5.1.H.264-EniaHD - [EHX].mkv,4506.01,919.87,20.4,1920x872,1920x872,2,30,CQ
tv,Extrapolations,Extrapolations.S01E06.2066.Lola.1080p.ATVP.WEB-DL.DDP5.1.H.264-EniaHD - [EHX].mkv,5080.34,886.64,17.5,1920x872,1920x872,2,30,CQ
tv,Extrapolations,Extrapolations.S01E03.2047.The.Fifth.Question.1080p.ATVP.WEB-DL.DDP5.1.H.264-EniaHD - [EHX].mkv,4775.68,964.11,20.2,1920x872,1920x872,2,30,CQ
tv,Extrapolations,Extrapolations.S01E01.2037.A.Raven.Story.1080p.ATVP.WEB-DL.DDP5.1.H.264-EniaHD - [EHX].mkv,4308.18,1049.28,24.4,1920x872,1920x872,2,30,CQ
movie,N/A,Superman (2025) x265 EAC3 7.1 Bluray-1080p Ghost - [EHX].mkv,8364.57,4374.49,52.3,1920x1080,1920x1080,4,32,CQ
anime,The Case Study of Vanitas (2021),[sam] Vanitas no Carte - 02 [BD 1080p FLAC] [8822B4BC] - [EHX].mkv,1662.26,248.98,15.0,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),[sam] Vanitas no Carte - 03 [BD 1080p FLAC] [BDE63D2B] - [EHX].mkv,1540.47,251.65,16.3,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),[sam] Vanitas no Carte - 04 [BD 1080p FLAC] [4B388837] - [EHX].mkv,1841.37,303.34,16.5,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),[sam] Vanitas no Carte - 05 [BD 1080p FLAC] [03D15E74] - [EHX].mkv,1533.1,279.44,18.2,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),[sam] Vanitas no Carte - 06 [BD 1080p FLAC] [8498E1EE] - [EHX].mkv,1851.33,343.69,18.6,1920x1080,1920x1080,2,32,CQ
movie,N/A,xXx - Return of Xander Cage (2017) x264 TrueHD Atmos 7.1 Bluray-1080p DDR - [EHX].mkv,14066.19,3230.54,23.0,1920x800,1920x800,3,32,CQ
anime,The Case Study of Vanitas (2021),[sam] Vanitas no Carte - 07 [BD 1080p FLAC] [B24C2A72] - [EHX].mkv,1252.42,256.39,20.5,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),[sam] Vanitas no Carte - 08 [BD 1080p FLAC] [133E6216] - [EHX].mkv,1340.99,214.87,16.0,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),[sam] Vanitas no Carte - 09 [BD 1080p FLAC] [90B63B29] - [EHX].mkv,1431.18,246.63,17.2,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),[sam] Vanitas no Carte - 10 [BD 1080p FLAC] [9EB21FD3] - [EHX].mkv,1478.88,280.75,19.0,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),[sam] Vanitas no Carte - 11 [BD 1080p FLAC] [39EC1E6A] - [EHX].mkv,1271.25,245.56,19.3,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),[sam] Vanitas no Carte - 12 [BD 1080p FLAC] [41A14681] - [EHX].mkv,1557.74,243.59,15.6,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),The Case Study of Vanitas - S01E01 - Mémoire 1 - Vanitas ―In the Event of Rusty Hopes― x265 FLAC Bluray-1080p sam - [EHX].mkv,2254.26,369.91,16.4,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),The Case Study of Vanitas - S01E13 - Mémoire 13 - Forêt d'argent ―A Chance Encounter― x265 FLAC Bluray-1080p sam - [EHX].mkv,1769.7,384.47,21.7,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),The Case Study of Vanitas - S01E14 - Mémoire 14 - Château de sorciére ―The Witch and the Young Man― x265 FLAC Bluray-1080p sam - [EHX].mkv,1526.89,251.6,16.5,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),The Case Study of Vanitas - S01E15 - Mémoire 15 - Oiseau et ciel ―The d'Apchiers' Vampire― x265 FLAC Bluray-1080p sam - [EHX].mkv,1659.94,288.39,17.4,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),The Case Study of Vanitas - S01E16 - Mémoire 16 - Chasse aux vampires ―The Beast― x265 FLAC Bluray-1080p sam - [EHX].mkv,1893.39,312.18,16.5,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),The Case Study of Vanitas - S01E17 - Mémoire 17 - Vengeance ―Hands Upon a Nightmare― x265 FLAC Bluray-1080p sam - [EHX].mkv,2111.03,365.15,17.3,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),The Case Study of Vanitas - S01E18 - Mémoire 18 - Avec toi ―Just the Two of Us― x265 FLAC Bluray-1080p sam - [EHX].mkv,1620.1,286.11,17.7,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),The Case Study of Vanitas - S01E19 - Mémoire 19 - Canorus ―Snow Flower― x265 FLAC Bluray-1080p sam - [EHX].mkv,1876.92,395.46,21.1,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),The Case Study of Vanitas - S01E20 - Mémoire 20 - Mal d'amour ―The Incurable Disease― x265 FLAC Bluray-1080p sam - [EHX].mkv,1336.24,234.84,17.6,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),The Case Study of Vanitas - S01E21 - Mémoire 21 - Un autre ―Scar― x265 FLAC Bluray-1080p sam - [EHX].mkv,1359.08,229.23,16.9,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),The Case Study of Vanitas - S01E22 - Mémoire 22 - Rencontre ―Blue Night― x265 FLAC Bluray-1080p sam - [EHX].mkv,1448.32,223.05,15.4,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),The Case Study of Vanitas - S01E23 - Mémoire 23 - Pleuvoir ―Tears like Rain― x265 FLAC Bluray-1080p sam - [EHX].mkv,2601.41,396.97,15.3,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),The Case Study of Vanitas - S01E24 - Mémoire 24 - Après la pluie ―His Wish― x265 FLAC Bluray-1080p sam - [EHX].mkv,1991.11,303.87,15.3,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),[sam] Vanitas no Carte - NCED 1 [BD 1080p FLAC] [F919673C] - [EHX].mkv,115.5,10.38,9.0,1920x1080,1920x1080,2,32,CQ
anime,The Case Study of Vanitas (2021),[sam] Vanitas no Carte - NCOP 1 [BD 1080p FLAC] [ACE65BAF] - [EHX].mkv,99.75,21.82,21.9,1920x1080,1920x1080,1,32,CQ
anime,Sword of the Demon Hunter - Kijin Gentosho (2025),Sword of the Demon Hunter - Kijin Gentosho - S01E01 - Demons and Humans x264 AAC WEBDL-1080p VARYG - [EHX].mkv,2230.88,482.25,21.6,1920x1080,1920x1080,2,32,CQ
anime,Sword of the Demon Hunter - Kijin Gentosho (2025),Sword of the Demon Hunter - Kijin Gentosho - S01E02 - The Demon's Daughter x264 AAC WEBDL-1080p VARYG - [EHX].mkv,975.24,165.89,17.0,1920x1080,1920x1080,2,32,CQ
anime,Sword of the Demon Hunter - Kijin Gentosho (2025),Sword of the Demon Hunter - Kijin Gentosho - S01E03 - The Devourer (Part 1) x264 AAC WEBDL-1080p VARYG - [EHX].mkv,992.22,169.73,17.1,1920x1080,1920x1080,2,32,CQ
anime,Sword of the Demon Hunter - Kijin Gentosho (2025),Sword of the Demon Hunter - Kijin Gentosho - S01E04 - The Devourer (Part 2) x264 AAC WEBDL-1080p VARYG - [EHX].mkv,991.38,174.69,17.6,1920x1080,1920x1080,2,32,CQ
anime,Sword of the Demon Hunter - Kijin Gentosho (2025),Sword of the Demon Hunter - Kijin Gentosho - S01E05 - The Garden of Happiness (Part 1) x264 AAC WEBDL-1080p VARYG - [EHX].mkv,990.18,199.98,20.2,1920x1080,1920x1080,2,32,CQ
anime,Sword of the Demon Hunter - Kijin Gentosho (2025),Sword of the Demon Hunter - Kijin Gentosho - S01E06 - The Garden of Happiness (Part 2) x264 AAC WEBDL-1080p VARYG - [EHX].mkv,988.98,203.36,20.6,1920x1080,1920x1080,2,32,CQ
anime,Sword of the Demon Hunter - Kijin Gentosho (2025),Sword of the Demon Hunter - Kijin Gentosho - S01E07 - The Haunting of Kudanzaka x264 AAC WEBDL-1080p VARYG - [EHX].mkv,989.27,168.54,17.0,1920x1080,1920x1080,2,32,CQ
anime,Sword of the Demon Hunter - Kijin Gentosho (2025),Sword of the Demon Hunter - Kijin Gentosho - S01E08 - Blossoming Dreams of the Kanzashi (Part 1) x264 AAC WEBDL-1080p VARYG - [EHX].mkv,989.56,208.83,21.1,1920x1080,1920x1080,2,32,CQ
anime,Sword of the Demon Hunter - Kijin Gentosho (2025),Sword of the Demon Hunter - Kijin Gentosho - S01E09 - Blossoming Dreams of the Kanzashi (Part 2) x264 AAC WEBDL-1080p VARYG - [EHX].mkv,988.53,204.37,20.7,1920x1080,1920x1080,2,32,CQ
anime,Sword of the Demon Hunter - Kijin Gentosho (2025),Sword of the Demon Hunter - Kijin Gentosho - S01E10 - Harlot in the Rain x264 AAC WEBDL-1080p VARYG - [EHX].mkv,990.33,202.56,20.5,1920x1080,1920x1080,2,32,CQ
anime,Sword of the Demon Hunter - Kijin Gentosho (2025),Sword of the Demon Hunter - Kijin Gentosho - S01E11 - Drunken Dreams of Lingering Snow (Part 1) x264 AAC WEBDL-1080p VARYG - [EHX].mkv,992.92,172.67,17.4,1920x1080,1920x1080,2,32,CQ
anime,Sword of the Demon Hunter - Kijin Gentosho (2025),Sword of the Demon Hunter - Kijin Gentosho - S01E12 - Drunken Dreams of Lingering Snow (Part 2) x264 AAC WEBDL-1080p VARYG - [EHX].mkv,989.34,199.49,20.2,1920x1080,1920x1080,2,32,CQ
movie,N/A,A New Era DC Takes Off.mkv,151.36,57.92,38.3,1920x1080,1280x720,1,34,CQ
movie,N/A,Adventures in the Making of “Superman”.mkv,1885.82,608.37,32.3,1920x1080,1280x720,1,34,CQ
movie,N/A,Breaking News The Daily Planet Returns.mkv,174.45,70.86,40.6,1920x1080,1280x720,1,34,CQ
movie,N/A,Icons Forever Supermans Enduring Legacy.mkv,196.44,73.29,37.3,1920x1080,1280x720,1,34,CQ
movie,N/A,Krypto Short School Bus Scuffle.mkv,169.9,55.95,32.9,1920x1080,1280x720,1,34,CQ
movie,N/A,Kryptunes The Music of “Superman”.mkv,209.9,75.31,35.9,1920x1080,1280x720,1,34,CQ
movie,N/A,Lex Luthor The Mind of a Master Villain.mkv,172.32,62.11,36.0,1920x1080,1280x720,1,34,CQ
movie,N/A,Pawns to Pixels Krypto Is Born.mkv,185.04,69.84,37.7,1920x1080,1280x720,1,34,CQ
movie,N/A,The Justice Gang.mkv,338.06,114.74,33.9,1920x1080,1280x720,1,34,CQ
movie,N/A,The Ultimate Villain.mkv,164.36,74.57,45.4,1920x1080,1280x720,1,34,CQ
movie,N/A,2026-01-03 09-23-11 - [EHX].mkv,845.34,133.54,15.8,3840x2160,1920x1080,1,32,CQ
movie,N/A,Pirates of the Caribbean - Dead Man's Chest (2006) (1080p BluRay x265 10bit Tigole) - [EHX].mkv,9134.59,4091.91,44.8,1920x806,1920x806,2,27,CQ
movie,N/A,Pirates of the Caribbean - Dead Man's Chest (2006) (1080p BluRay x265 10bit Tigole) - Copy - [EHX].mkv,9134.59,3632.85,39.8,1920x806,1920x806,0,28,CQ
movie,N/A,Pirates of the Caribbean - At World's End (2007) (1080p BluRay x265 10bit Tigole) - [EHX].mkv,9795.92,3764.59,38.4,1920x800,1920x800,1,28,CQ
movie,N/A,Pirates of the Caribbean - The Curse of the Black Pearl (2003) (1080p BluRay x265 10bit Tigole) - [EHX].mkv,8626.32,3549.92,41.2,1920x800,1920x800,4,28,CQ
movie,N/A,Pirates of the Caribbean - Dead Men Tell No Tales (2017) (1080p BluRay x265 10bit Tigole) - [EHX].mkv,8415.25,2889.53,34.3,1920x800,1920x800,1,28,CQ
movie,N/A,Deleted Scenes.mkv,67.72,51.41,75.9,1920x800,1920x800,1,28,CQ
movie,N/A,Gallery - Jerry Bruckheimer Photo Diary.mkv,33.16,25.72,77.6,1920x1080,1920x1080,1,28,CQ
movie,N/A,Pirates of the Caribbean - On Stranger Tides (2011) (1080p BluRay x265 10bit Tigole) - [EHX].mkv,7070.29,2401.09,34.0,1920x800,1920x800,2,28,CQ
tv,Dimension 20,Dimension 20 - S27E01 - Welcome to the Wastes - [EHX].mkv,3929.25,1665.15,42.4,1920x1080,1920x1080,1,32,CQ
movie,N/A,Superman (2025) x265 EAC3 7.1 Bluray-1080p Ghost - Copy - [EHX].mkv,8364.57,3891.43,46.5,1920x1080,1920x1080,4,28,CQ
movie,N/A,The Roundup (2022) x265 AAC 5.1 Bluray-1080p Tigole - [EHX].mkv,5702.94,2383.35,41.8,1920x804,1920x804,2,28,CQ
movie,N/A,Wolf Children (2012) x264 AC3 5.1 Bluray-1080p RH - [EHX].mkv,4453.91,1929.93,43.3,1920x1080,1920x1080,3,32,CQ
movie,N/A,The Intern (2015) x265 AAC 5.1 Bluray-1080p Tigole - [EHX].mkv,5020.75,2681.95,53.4,1920x1080,1920x1080,1,28,CQ
movie,N/A,The.Suicide.Squad.2021.1080p.HMAX.WEB-DL.DDP5.1.Atmos.X.264-EVO - [EHX].mkv,5220.52,3193.39,61.2,1920x1012,1920x1012,1,32,CQ
movie,N/A,Venom - The Last Dance (2024) x265 EAC3 5.1 Bluray-1080p Radarr - [EHX].mkv,5722.93,1798.15,31.4,1920x804,1920x804,1,28,CQ
movie,N/A,Meet the Fockers (2004) 1080p 10bit Bluray x265 HEVC [Org DD 5.1 Hindi + DD 5.1 English] MSubs ~ TombDoc - [EHX].mkv,4453.37,1639.66,36.8,1920x1080,1280x720,2,30,CQ
movie,N/A,Meet the Parents (2000) 1080p 10bit Bluray x265 HEVC [Org DD 5.1 Hindi + DD 5.1 English] MSubs ~ TombDoc - [EHX].mkv,4190.09,1167.67,27.9,1920x1080,1280x720,2,30,CQ
movie,N/A,Little Fockers (2010) 1080p 10bit Bluray x265 HEVC [Org DD 5.1 Hindi + DD 5.1 English] MSubs ~ TombDoc - [EHX].mkv,3753.92,934.35,24.9,1920x1040,1280x720,2,30,CQ
movie,N/A,Premium Rush (2012) x265 AAC 5.1 Bluray-1080p afm72 - [EHX].mkv,4184.15,2174.93,52.0,1920x800,1920x1080,1,28,CQ
movie,N/A,Captain America - Brave New World (2025) x265 EAC3 7.1 Bluray-1080p Silence - [EHX].mkv,5999.46,2240.25,37.3,1920x800,1920x800,3,28,CQ
movie,N/A,A New Era DC Takes Off.mkv,151.36,112.09,74.1,1920x1080,1920x1080,1,28,CQ
movie,N/A,Adventures in the Making of “Superman”.mkv,1885.82,1191.9,63.2,1920x1080,1920x1080,1,28,CQ
movie,N/A,Lex Luthor The Mind of a Master Villain.mkv,172.32,122.45,71.1,1920x1080,1920x1080,1,28,CQ
movie,N/A,Kryptunes The Music of “Superman”.mkv,209.9,155.56,74.1,1920x1080,1920x1080,1,28,CQ
movie,N/A,Breaking News The Daily Planet Returns.mkv,174.45,139.55,80.0,1920x1080,1920x1080,1,28,CQ

Can't render this file because it has a wrong number of fields in line 14.

View File

@ -42,8 +42,8 @@ def calculate_stream_bitrate(input_file: Path, stream_index: int) -> int:
str(input_file) str(input_file)
] ]
try: try:
probe_result = subprocess.run(probe_cmd, capture_output=True, text=True, check=False) probe_result = subprocess.run(probe_cmd, capture_output=True, text=True, encoding='utf-8', errors='ignore', check=False)
codec_name = probe_result.stdout.strip().lower() if probe_result.returncode == 0 else "aac" codec_name = probe_result.stdout.strip().lower() if probe_result.stdout and probe_result.returncode == 0 else "aac"
except: except:
codec_name = "aac" codec_name = "aac"
@ -64,7 +64,7 @@ def calculate_stream_bitrate(input_file: Path, stream_index: int) -> int:
temp_audio_path temp_audio_path
] ]
logger.debug(f"Extracting audio stream {stream_index} ({codec_name}) to temporary file for bitrate calculation...") logger.debug(f"Extracting audio stream {stream_index} ({codec_name}) to temporary file for bitrate calculation...")
result = subprocess.run(extract_cmd, capture_output=True, text=True, check=False) result = subprocess.run(extract_cmd, capture_output=True, text=True, encoding='utf-8', errors='ignore', check=False)
# Check if extraction succeeded # Check if extraction succeeded
if result.returncode != 0: if result.returncode != 0:
@ -76,7 +76,8 @@ def calculate_stream_bitrate(input_file: Path, stream_index: int) -> int:
# Step 2: Parse bitrate from ffmpeg's output (stderr) # Step 2: Parse bitrate from ffmpeg's output (stderr)
# Look for line like: "bitrate= 457.7kbits/s" # Look for line like: "bitrate= 457.7kbits/s"
bitrate_kbps = 0 bitrate_kbps = 0
for line in result.stderr.split("\n"): stderr_lines = result.stderr if result.stderr else ""
for line in stderr_lines.split("\n"):
if "bitrate=" in line: if "bitrate=" in line:
# Extract bitrate value from line like "size= 352162KiB time=01:45:03.05 bitrate= 457.7kbits/s" # Extract bitrate value from line like "size= 352162KiB time=01:45:03.05 bitrate= 457.7kbits/s"
parts = line.split("bitrate=") parts = line.split("bitrate=")
@ -101,11 +102,14 @@ def calculate_stream_bitrate(input_file: Path, stream_index: int) -> int:
"-of", "default=noprint_wrappers=1:nokey=1:noprint_wrappers=1", "-of", "default=noprint_wrappers=1:nokey=1:noprint_wrappers=1",
temp_audio_path temp_audio_path
] ]
duration_result = subprocess.run(duration_cmd, capture_output=True, text=True, check=True) duration_result = subprocess.run(duration_cmd, capture_output=True, text=True, encoding='utf-8', errors='ignore', check=False)
duration_seconds = float(duration_result.stdout.strip()) try:
duration_seconds = float(duration_result.stdout.strip()) if duration_result.stdout else 1.0
bitrate_kbps = int((file_size_bytes * 8) / duration_seconds / 1000) bitrate_kbps = int((file_size_bytes * 8) / duration_seconds / 1000)
logger.debug(f"Stream {stream_index}: Calculated bitrate from file: {bitrate_kbps} kbps") logger.debug(f"Stream {stream_index}: Calculated bitrate from file: {bitrate_kbps} kbps")
except (ValueError, ZeroDivisionError):
logger.warning(f"Stream {stream_index}: Could not parse duration from ffprobe")
return 0
return bitrate_kbps return bitrate_kbps
@ -126,21 +130,55 @@ def calculate_stream_bitrate(input_file: Path, stream_index: int) -> int:
def get_audio_streams(input_file: Path): def get_audio_streams(input_file: Path):
""" """
Detect audio streams and calculate robust bitrates by extracting each stream. Detect audio streams and calculate robust bitrates by extracting each stream.
Returns list of (index, channels, calculated_bitrate_kbps, language, metadata_bitrate_kbps) Returns list of (index, channels, calculated_bitrate_kbps, language, metadata_bitrate_kbps, title)
""" """
import re
# First, get full ffprobe output to extract language codes and titles
probe_cmd = ["ffprobe", "-v", "info", str(input_file)]
probe_result = subprocess.run(probe_cmd, capture_output=True, text=True, encoding='utf-8', errors='ignore')
# Parse language and title from output
language_map = {}
title_map = {}
stderr_output = probe_result.stderr if probe_result.stderr else ""
for line in stderr_output.split("\n"):
# Match "Stream #0:X(YYY)" where X is stream number, YYY is language
match = re.search(r"Stream #0:(\d+)\((\w{3})\)", line)
if match:
stream_idx = int(match.group(1))
lang_code = match.group(2)
language_map[stream_idx] = lang_code
# Get audio stream details via JSON with tags
cmd = [ cmd = [
"ffprobe","-v","error","-select_streams","a", "ffprobe","-v","error","-select_streams","a",
"-show_entries","stream=index,channels,bit_rate,tags=language", "-show_entries","stream=index,channels,bit_rate,tags",
"-of","json", str(input_file) "-of","json", str(input_file)
] ]
result = subprocess.run(cmd, capture_output=True, text=True) result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8', errors='ignore')
data = json.loads(result.stdout) try:
data = json.loads(result.stdout) if result.stdout else {"streams": []}
except (json.JSONDecodeError, TypeError):
data = {"streams": []}
streams = [] streams = []
for stream_num, s in enumerate(data.get("streams", [])): for stream_num, s in enumerate(data.get("streams", [])):
index = s["index"] index = s["index"]
channels = s.get("channels", 2) channels = s.get("channels", 2)
src_lang = s.get("tags", {}).get("language", "und")
# Get language from our parsed map, default to "und"
src_lang = language_map.get(index, "und")
# Get title from tags or from our parsed map
title = ""
if "tags" in s and "title" in s["tags"]:
title = s["tags"]["title"]
elif index in title_map:
title = title_map[index]
bit_rate_meta = int(s.get("bit_rate", 0)) if s.get("bit_rate") else 0 bit_rate_meta = int(s.get("bit_rate", 0)) if s.get("bit_rate") else 0
# Calculate robust bitrate by extracting the audio stream # Calculate robust bitrate by extracting the audio stream
@ -151,7 +189,7 @@ def get_audio_streams(input_file: Path):
calculated_bitrate_kbps = int(bit_rate_meta / 1000) if bit_rate_meta else 160 calculated_bitrate_kbps = int(bit_rate_meta / 1000) if bit_rate_meta else 160
logger.info(f"Stream {index}: Using fallback bitrate {calculated_bitrate_kbps} kbps") logger.info(f"Stream {index}: Using fallback bitrate {calculated_bitrate_kbps} kbps")
streams.append((index, channels, calculated_bitrate_kbps, src_lang, int(bit_rate_meta / 1000) if bit_rate_meta else 0)) streams.append((index, channels, calculated_bitrate_kbps, src_lang, int(bit_rate_meta / 1000) if bit_rate_meta else 0, title))
return streams return streams
@ -216,3 +254,188 @@ def choose_audio_bitrate(channels: int, bitrate_kbps: int, audio_config: dict, i
else: else:
# Medium and above, use medium with EAC3 # Medium and above, use medium with EAC3
return ("eac3", medium_br) return ("eac3", medium_br)
def filter_audio_streams(input_file: Path, streams: list) -> list:
"""
Filter audio streams to keep only best English audio + Commentary tracks.
Args:
input_file: Path to video file
streams: List of (index, channels, bitrate, language, metadata, title) tuples
Returns:
Filtered list of streams (original indices preserved for FFmpeg mapping)
"""
if not streams:
return streams
# Try to get stream metadata (title) to detect commentary
english_tracks = []
commentary_tracks = []
for stream_info in streams:
index, channels, bitrate, language, metadata, title = stream_info
# Check if commentary (in title or metadata)
is_commentary = "comment" in str(title).lower() or "comment" in str(metadata).lower()
# Determine if English (check language field or assume first is English if no language set)
is_english = (language and "eng" in language.lower()) or (not language)
if is_commentary:
commentary_tracks.append((index, channels, bitrate, stream_info))
elif is_english:
english_tracks.append((index, channels, bitrate, stream_info))
# If no English tracks, return original
if not english_tracks:
logger.info("No English audio tracks detected - keeping all audio")
return streams
# Pick best English track (most channels, then highest bitrate)
english_tracks.sort(key=lambda x: (-x[1], -x[2])) # Sort by channels desc, then bitrate desc
best_english = english_tracks[0][3] # Get original stream tuple
logger.info(f"Audio filter: Keeping best English track (index {best_english[0]}: {best_english[1]}ch @ {best_english[2]}kbps)")
# Build result: best English + all commentary
filtered = [best_english] + [ct[3] for ct in commentary_tracks]
if commentary_tracks:
logger.info(f"Audio filter: Also keeping {len(commentary_tracks)} commentary track(s)")
# Log removed tracks
removed_count = len(streams) - len(filtered)
if removed_count > 0:
logger.info(f"Audio filter: Removed {removed_count} non-English audio track(s)")
return filtered
def prompt_user_audio_selection(streams: list) -> list:
"""
Interactively prompt user to select which audio streams to keep.
Args:
streams: List of (index, channels, bitrate, language, metadata, title) tuples
Returns:
Filtered list containing only selected streams
"""
if not streams or len(streams) <= 1:
return streams
print("\n" + "="*80)
print("🎵 AUDIO STREAM SELECTION")
print("="*80)
# Display all streams with details
for index, channels, bitrate, language, metadata, title in streams:
channels_display = f"{channels}ch"
lang_display = language if language != "und" else "undefined"
# Display title if available
if title:
title_display = f" | {title}"
else:
title_display = ""
print(f"\nStream #{index}: {channels_display} | Lang: {lang_display} | Bitrate: {bitrate}kbps{title_display}")
print("\n" + "-"*80)
print("Enter stream numbers to keep (comma-separated, e.g.: 1,2 or just 2)")
print("Leave blank to keep all streams")
print("-"*80)
user_input = input("➜ Keep streams: ").strip()
# If empty, keep all
if not user_input:
print("✅ Keeping all audio streams\n")
return streams
# Parse user input
try:
selected_indices = set()
for part in user_input.split(","):
idx = int(part.strip())
selected_indices.add(idx)
except ValueError:
print("❌ Invalid input. Keeping all streams.")
logger.warning("User provided invalid audio selection input")
return streams
# Filter streams to only selected ones
filtered = [s for s in streams if s[0] in selected_indices]
if not filtered:
print("❌ No valid streams selected. Keeping all streams.")
logger.warning("User selected no valid streams")
return streams
# Log what was selected/removed
removed_count = len(streams) - len(filtered)
print(f"✅ Keeping {len(filtered)} stream(s), removing {removed_count} stream(s)\n")
logger.info(f"User selected {len(filtered)} audio stream(s): {[s[0] for s in filtered]}")
if removed_count > 0:
removed_indices = [s[0] for s in streams if s[0] not in selected_indices]
logger.info(f"Removed {removed_count} audio stream(s): {removed_indices}")
# Return filtered streams without strip_title field - let prompt_for_title_stripping handle that
return filtered
def prompt_for_title_stripping(filtered_streams: list) -> list:
"""
Prompt user to select which streams should have titles stripped.
Args:
filtered_streams: List of (index, channels, bitrate, language, metadata, title, strip_title) tuples
Returns:
Same list with strip_title field updated based on user selection
"""
streams_with_titles = [(s[0], s[5]) for s in filtered_streams if s[5]]
if not streams_with_titles:
return [s + (False,) if len(s) == 6 else s for s in filtered_streams]
print("\n" + "="*80)
print("📝 TITLE METADATA STRIPPING (Optional)")
print("="*80)
print("\nStreams with titles that can be stripped:\n")
for idx, title in streams_with_titles:
print(f" Stream #{idx}: \"{title}\"")
print("\n" + "-"*80)
print("Enter stream numbers to STRIP titles (comma-separated, or leave blank to keep all)")
print("Example: \"1,3\" will strip titles from streams #1 and #3")
print("-"*80)
strip_input = input("➜ Strip titles from: ").strip()
strip_indices = set()
if strip_input:
try:
for part in strip_input.split(","):
idx = int(part.strip())
strip_indices.add(idx)
except ValueError:
print("❌ Invalid input. Keeping all titles.\n")
logger.warning("Invalid title stripping input")
# Add strip_title field to each stream
result = []
for s in filtered_streams:
should_strip = s[0] in strip_indices
result.append(s + (should_strip,))
if strip_indices:
print(f"✅ Will strip titles from stream(s): {sorted(list(strip_indices))}\n")
logger.info(f"User selected to strip titles from streams: {sorted(list(strip_indices))}")
else:
print("✅ Keeping all titles\n")
return result

View File

@ -91,35 +91,47 @@ def load_config_xml(path: Path) -> dict:
if encode_elem is not None: if encode_elem is not None:
cq_elem = encode_elem.find("cq") cq_elem = encode_elem.find("cq")
if cq_elem is not None: if cq_elem is not None:
# Check if CQ has encoder-specific sub-elements (av1, hevc)
encoder_elems = list(cq_elem)
if encoder_elems and encoder_elems[0].tag in ["av1", "hevc"]:
# New nested structure with encoder-specific CQ values
for encoder_tag in cq_elem:
if encoder_tag.tag in ["av1", "hevc"]:
cq[encoder_tag.tag] = {}
for child in encoder_tag:
if child.text and child.text.strip():
cq[encoder_tag.tag][child.tag] = int(child.text.strip())
else:
# Old flat structure (backwards compatibility)
for child in cq_elem: for child in cq_elem:
if child.text: if child.text and child.text.strip():
cq[child.tag] = int(child.text) cq[child.tag] = int(child.text.strip())
fallback_elem = encode_elem.find("fallback") fallback_elem = encode_elem.find("fallback")
if fallback_elem is not None: if fallback_elem is not None:
for child in fallback_elem: for child in fallback_elem:
if child.text: if child.text and child.text.strip():
fallback[child.tag] = child.text fallback[child.tag] = child.text.strip()
filters_elem = encode_elem.find("filters") filters_elem = encode_elem.find("filters")
if filters_elem is not None: if filters_elem is not None:
for child in filters_elem: for child in filters_elem:
if child.text: if child.text and child.text.strip():
filters[child.tag] = child.text filters[child.tag] = child.text.strip()
# --- Audio --- # --- Audio ---
audio = {"stereo": {}, "multi_channel": {}} audio = {"stereo": {}, "multi_channel": {}}
stereo_elem = root.find("audio/stereo") stereo_elem = root.find("audio/stereo")
if stereo_elem is not None: if stereo_elem is not None:
for child in stereo_elem: for child in stereo_elem:
if child.text: if child.text and child.text.strip():
audio["stereo"][child.tag] = int(child.text) audio["stereo"][child.tag] = int(child.text.strip())
multi_elem = root.find("audio/multi_channel") multi_elem = root.find("audio/multi_channel")
if multi_elem is not None: if multi_elem is not None:
for child in multi_elem: for child in multi_elem:
if child.text: if child.text and child.text.strip():
audio["multi_channel"][child.tag] = int(child.text) audio["multi_channel"][child.tag] = int(child.text.strip())
# --- Services (Sonarr/Radarr) --- # --- Services (Sonarr/Radarr) ---
services = {"sonarr": {}, "radarr": {}} services = {"sonarr": {}, "radarr": {}}

View File

@ -4,7 +4,7 @@
import subprocess import subprocess
from pathlib import Path from pathlib import Path
from core.audio_handler import get_audio_streams, choose_audio_bitrate from core.audio_handler import get_audio_streams, choose_audio_bitrate, filter_audio_streams, prompt_user_audio_selection, prompt_for_title_stripping
from core.logger_helper import setup_logger from core.logger_helper import setup_logger
logger = setup_logger(Path(__file__).parent.parent / "logs") logger = setup_logger(Path(__file__).parent.parent / "logs")
@ -12,7 +12,8 @@ logger = setup_logger(Path(__file__).parent.parent / "logs")
def run_ffmpeg(input_file: Path, output_file: Path, cq: int, scale_width: int, scale_height: int, def run_ffmpeg(input_file: Path, output_file: Path, cq: int, scale_width: int, scale_height: int,
src_width: int, src_height: int, filter_flags: str, audio_config: dict, src_width: int, src_height: int, filter_flags: str, audio_config: dict,
method: str, bitrate_config: dict, subtitle_file: Path = None, audio_language: str = None): method: str, bitrate_config: dict, encoder: str = "nvenc", subtitle_file: Path = None, audio_language: str = None,
audio_filter_config: dict = None, test_mode: bool = False):
""" """
Run FFmpeg encode with comprehensive logging. Run FFmpeg encode with comprehensive logging.
@ -20,15 +21,65 @@ def run_ffmpeg(input_file: Path, output_file: Path, cq: int, scale_width: int, s
""" """
streams = get_audio_streams(input_file) streams = get_audio_streams(input_file)
# Apply audio filter if enabled
if audio_filter_config and audio_filter_config.get("enabled", False):
# Check if pre-selected streams provided
if audio_filter_config.get("preselected"):
# Use pre-selected streams (skip interactive)
preselected_str = audio_filter_config["preselected"]
try:
selected_indices = set()
for part in preselected_str.split(","):
idx = int(part.strip())
selected_indices.add(idx)
# Filter to only selected streams
streams = [s for s in streams if s[0] in selected_indices]
# Add strip_title field (False by default for pre-selected)
streams = [s + (False,) for s in streams]
logger.info(f"Pre-selected audio streams: {[s[0] for s in streams]}")
except ValueError:
logger.warning(f"Invalid audio_select format: {preselected_str}. Using all streams.")
streams = [s + (False,) for s in streams]
else:
# Check if interactive mode requested (via --filter-audio CLI flag)
# If audio_filter_config came from CLI, it has "interactive": True
if "interactive" in audio_filter_config and audio_filter_config.get("interactive", False):
# Interactive audio selection (show prompt to user)
streams = prompt_user_audio_selection(streams)
# Prompt for title stripping after stream selection
streams = prompt_for_title_stripping(streams)
else:
# Automatic filtering from config (keep best English + Commentary)
streams = filter_audio_streams(input_file, streams)
# Add strip_title field (False by default for automatic filtering)
streams = [s + (False,) for s in streams]
else:
# No filtering - add strip_title field as False
streams = [s + (False,) for s in streams]
# Log comprehensive encode settings # Log comprehensive encode settings
header = f"\n🧩 ENCODE SETTINGS" header = f"\n🧩 ENCODE SETTINGS"
logger.info(header) logger.info(header)
print(" ") print(" ")
# Determine encoder display name and settings
if encoder == "av1":
encoder_name = "AV1 NVENC"
encoder_codec = "av1_nvenc"
encoder_preset = "p7"
encoder_pix_fmt = "yuv420p"
encoder_bit_depth = "8-bit"
else: # default hevc = HEVC NVENC
encoder_name = "HEVC NVENC"
encoder_codec = "hevc_nvenc"
encoder_preset = "slow"
encoder_pix_fmt = "p010le"
encoder_bit_depth = "10-bit"
logger.info(f" Video:") logger.info(f" Video:")
logger.info(f" • Source Resolution: {src_width}x{src_height}") logger.info(f" • Source Resolution: {src_width}x{src_height}")
logger.info(f" • Target Resolution: {scale_width}x{scale_height}") logger.info(f" • Target Resolution: {scale_width}x{scale_height}")
logger.info(f" • Encoder: av1_nvenc (preset p1, pix_fmt p010le)") logger.info(f" • Encoder: {encoder_name} (preset {encoder_preset}, {encoder_bit_depth}, pix_fmt {encoder_pix_fmt})")
logger.info(f" • Scale Filter: {filter_flags}") logger.info(f" • Scale Filter: {filter_flags}")
logger.info(f" • Encode Method: {method}") logger.info(f" • Encode Method: {method}")
if method == "CQ": if method == "CQ":
@ -42,7 +93,7 @@ def run_ffmpeg(input_file: Path, output_file: Path, cq: int, scale_width: int, s
logger.info(f" Audio Streams ({len(streams)} detected):") logger.info(f" Audio Streams ({len(streams)} detected):")
print(" ") print(" ")
for (index, channels, avg_bitrate, src_lang, meta_bitrate) in streams: for (index, channels, avg_bitrate, src_lang, meta_bitrate, title, strip_title) in streams:
# Normalize to 2ch or 6ch output # Normalize to 2ch or 6ch output
is_1080_class = scale_height >= 1080 or scale_width >= 1920 is_1080_class = scale_height >= 1080 or scale_width >= 1920
output_channels = 6 if is_1080_class and channels >= 6 else 2 output_channels = 6 if is_1080_class and channels >= 6 else 2
@ -55,7 +106,9 @@ def run_ffmpeg(input_file: Path, output_file: Path, cq: int, scale_width: int, s
action = "ENCODE" action = "ENCODE"
bitrate_display = f"{br/1000:.0f}kbps" bitrate_display = f"{br/1000:.0f}kbps"
line = f" - Stream #{index}: {channels}ch→{output_channels}ch | Lang: {src_lang} | Detected: {avg_bitrate}kbps | Action: {action} | Target: {bitrate_display}" # Include title in display if present
title_info = f" | Title: {title}" if title else ""
line = f" - Stream #{index}: {channels}ch→{output_channels}ch | Lang: {src_lang} | Detected: {avg_bitrate}kbps | Action: {action} | Target: {bitrate_display}{title_info}"
print(line) print(line)
logger.info(line) logger.info(line)
@ -65,9 +118,17 @@ def run_ffmpeg(input_file: Path, output_file: Path, cq: int, scale_width: int, s
if subtitle_file: if subtitle_file:
cmd.extend(["-i", str(subtitle_file)]) cmd.extend(["-i", str(subtitle_file)])
# In test mode, only encode first 15 minutes
if test_mode:
cmd.extend(["-t", "900"]) # 900 seconds = 15 minutes
cmd.extend([ cmd.extend([
"-vf",f"scale={scale_width}:{scale_height}:flags={filter_flags}:force_original_aspect_ratio=decrease", "-vf",f"scale={scale_width}:{scale_height}:flags={filter_flags}:force_original_aspect_ratio=decrease",
"-map","0:v","-map","0:a"]) "-map","0:v"])
# Map only selected audio streams
for index, _, _, _, _, _, _ in streams:
cmd.extend(["-map", f"0:{index}"])
# Add subtitle mapping if present # Add subtitle mapping if present
if subtitle_file: if subtitle_file:
@ -76,7 +137,7 @@ def run_ffmpeg(input_file: Path, output_file: Path, cq: int, scale_width: int, s
cmd.extend(["-map", "0:s?"]) cmd.extend(["-map", "0:s?"])
cmd.extend([ cmd.extend([
"-c:v","av1_nvenc","-preset","p1","-pix_fmt","p010le"]) "-c:v", encoder_codec, "-preset", encoder_preset, "-pix_fmt", encoder_pix_fmt])
if method=="CQ": if method=="CQ":
cmd += ["-cq", str(cq)] cmd += ["-cq", str(cq)]
@ -88,7 +149,7 @@ def run_ffmpeg(input_file: Path, output_file: Path, cq: int, scale_width: int, s
bufsize = bitrate_config.get(f"bufsize_{res_key}", "1800k") bufsize = bitrate_config.get(f"bufsize_{res_key}", "1800k")
cmd += ["-b:v", vb, "-maxrate", maxrate, "-bufsize", bufsize] cmd += ["-b:v", vb, "-maxrate", maxrate, "-bufsize", bufsize]
for i, (index, channels, avg_bitrate, src_lang, meta_bitrate) in enumerate(streams): for i, (index, channels, avg_bitrate, src_lang, meta_bitrate, title, strip_title) in enumerate(streams):
# Normalize to 2ch or 6ch output # Normalize to 2ch or 6ch output
is_1080_class = scale_height >= 1080 or scale_width >= 1920 is_1080_class = scale_height >= 1080 or scale_width >= 1920
output_channels = 6 if is_1080_class and channels >= 6 else 2 output_channels = 6 if is_1080_class and channels >= 6 else 2
@ -100,6 +161,9 @@ def run_ffmpeg(input_file: Path, output_file: Path, cq: int, scale_width: int, s
# Only add language metadata if explicitly provided # Only add language metadata if explicitly provided
if audio_language: if audio_language:
cmd += [f"-metadata:s:a:{i}", f"language={audio_language}"] cmd += [f"-metadata:s:a:{i}", f"language={audio_language}"]
# Strip title metadata if requested
if strip_title:
cmd += [f"-metadata:s:a:{i}", "title="]
else: else:
# Re-encode with target bitrate # Re-encode with target bitrate
# EAC3 for multichannel, AAC for stereo # EAC3 for multichannel, AAC for stereo
@ -122,7 +186,9 @@ def run_ffmpeg(input_file: Path, output_file: Path, cq: int, scale_width: int, s
# Only add language metadata if explicitly provided # Only add language metadata if explicitly provided
if audio_language: if audio_language:
cmd += [f"-metadata:s:a:{i}", f"language={audio_language}"] cmd += [f"-metadata:s:a:{i}", f"language={audio_language}"]
# Strip title metadata if requested
if strip_title:
cmd += [f"-metadata:s:a:{i}", "title="]
# Add subtitle codec and metadata if subtitles are present # Add subtitle codec and metadata if subtitles are present
if subtitle_file: if subtitle_file:
cmd += ["-c:s", "srt", "-metadata:s:s:0", "language=eng"] cmd += ["-c:s", "srt", "-metadata:s:s:0", "language=eng"]

View File

@ -11,7 +11,7 @@ from pathlib import Path
from core.audio_handler import get_audio_streams from core.audio_handler import get_audio_streams
from core.encode_engine import run_ffmpeg from core.encode_engine import run_ffmpeg
from core.logger_helper import setup_logger, setup_failure_logger from core.logger_helper import setup_logger, setup_failure_logger
from core.video_handler import get_source_resolution, determine_target_resolution from core.video_handler import get_source_resolution, get_source_bit_depth, determine_target_resolution
logger = setup_logger(Path(__file__).parent.parent / "logs") logger = setup_logger(Path(__file__).parent.parent / "logs")
failure_logger = setup_failure_logger(Path(__file__).parent.parent / "logs") failure_logger = setup_failure_logger(Path(__file__).parent.parent / "logs")
@ -34,7 +34,7 @@ def _cleanup_temp_files(temp_input: Path, temp_output: Path):
logger.warning(f"Could not delete temp output {temp_output.name}: {e}") logger.warning(f"Could not delete temp output {temp_output.name}: {e}")
def process_folder(folder: Path, cq: int, transcode_mode: str, resolution: str, config: dict, tracker_file: Path, test_mode: bool = False, audio_language: str = None): def process_folder(folder: Path, cq: int, transcode_mode: str, resolution: str, config: dict, tracker_file: Path, test_mode: bool = False, audio_language: str = None, filter_audio: bool = None, audio_select: str = None, encoder: str = "hevc"):
""" """
Process all video files in folder with appropriate encoding settings. Process all video files in folder with appropriate encoding settings.
@ -47,6 +47,9 @@ def process_folder(folder: Path, cq: int, transcode_mode: str, resolution: str,
tracker_file: Path to CSV tracker file tracker_file: Path to CSV tracker file
test_mode: If True, only encode first file and skip final move/cleanup test_mode: If True, only encode first file and skip final move/cleanup
audio_language: Optional language code to tag audio (e.g., 'eng', 'spa'). If None, no tagging applied. audio_language: Optional language code to tag audio (e.g., 'eng', 'spa'). If None, no tagging applied.
filter_audio: If True, show interactive audio selection prompt. If None, use config setting.
audio_select: Pre-selected audio streams (comma-separated, e.g., "1,2"). Skips interactive prompt.
encoder: Video encoder to use - "hevc" for HEVC NVENC 10-bit (default) or "av1" for AV1 NVENC 8-bit.
""" """
if not folder.exists(): if not folder.exists():
print(f"❌ Folder not found: {folder}") print(f"❌ Folder not found: {folder}")
@ -67,30 +70,33 @@ def process_folder(folder: Path, cq: int, transcode_mode: str, resolution: str,
filter_flags = filters_config.get("default","lanczos") filter_flags = filters_config.get("default","lanczos")
folder_lower = str(folder).lower() folder_lower = str(folder).lower()
is_tv = "\\tv\\" in folder_lower or "/tv/" in folder_lower is_tv = "\\tv\\" in folder_lower or "/tv/" in folder_lower
is_anime = "\\anime\\" in folder_lower or "/anime/" in folder_lower
if is_tv: if is_tv:
filter_flags = filters_config.get("tv","bicubic") filter_flags = filters_config.get("tv","bicubic")
elif is_anime:
filter_flags = filters_config.get("anime", filters_config.get("default","lanczos"))
processing_folder = Path(config["processing_folder"]) processing_folder = Path(config["processing_folder"])
processing_folder.mkdir(parents=True, exist_ok=True) processing_folder.mkdir(parents=True, exist_ok=True)
# Determine if we're in smart mode (no explicit mode specified) # Determine encoding mode
is_smart_mode = transcode_mode not in ["cq", "bitrate"] # Default/smart mode is_smart_mode = transcode_mode == "compression" # Try CQ first, then bitrate fallback
is_forced_cq = transcode_mode == "cq" is_forced_cq = transcode_mode == "cq"
is_forced_bitrate = transcode_mode == "bitrate" is_forced_bitrate = transcode_mode == "bitrate"
# Track files for potential retry in smart mode # Track files for potential retry in smart mode
failed_cq_files = [] # List of (file_path, metadata) for CQ failures in smart mode failed_cq_files = [] # List of (file_path, metadata) for CQ failures in compression mode
consecutive_failures = 0 consecutive_failures = 0
max_consecutive = 3 max_consecutive = 3
# Phase 1: Process files with initial mode strategy # Phase 1: Process files with initial mode strategy
print(f"\n{'='*60}") print(f"\n{'='*60}")
if is_smart_mode: if is_smart_mode:
print("📋 MODE: Smart (Try CQ first, retry with Bitrate if needed)") print("📋 MODE: Compression (Try CQ first, retry with Bitrate if needed)")
elif is_forced_cq: elif is_forced_cq:
print("📋 MODE: Forced CQ (skip failures, log them)") print("📋 MODE: CQ (constant quality, skip failures, log them)")
else: else:
print("📋 MODE: Forced Bitrate (skip failures, log them)") print("📋 MODE: Bitrate (bitrate mode only, skip failures, log them)")
print(f"{'='*60}\n") print(f"{'='*60}\n")
skipped_count = 0 skipped_count = 0
@ -111,10 +117,28 @@ def process_folder(folder: Path, cq: int, transcode_mode: str, resolution: str,
print(f"📁 Processing: {file.name}") print(f"📁 Processing: {file.name}")
temp_input = (processing_folder / file.name).resolve() temp_input = (processing_folder / file.name).resolve()
# Check if file already exists in processing folder
if temp_input.exists() and os.access(temp_input, os.R_OK):
source_size = file.stat().st_size
temp_size = temp_input.stat().st_size
# Verify it's complete (same size as source)
if source_size == temp_size:
print(f"✓ Found existing copy in processing folder (verified complete)")
logger.info(f"File already in processing: {file.name} ({temp_size/1e6:.2f} MB verified complete)")
else:
# File exists but incomplete - recopy
print(f"⚠️ Existing copy incomplete ({temp_size/1e6:.2f} MB vs {source_size/1e6:.2f} MB source). Re-copying...")
logger.warning(f"Incomplete copy detected for {file.name}. Re-copying.")
shutil.copy2(file, temp_input)
logger.info(f"Re-copied {file.name}{temp_input.name}")
else:
# File doesn't exist or not accessible - copy it
shutil.copy2(file, temp_input) shutil.copy2(file, temp_input)
logger.info(f"Copied {file.name}{temp_input.name}") logger.info(f"Copied {file.name}{temp_input.name}")
# Verify file was copied and is accessible # Verify file is accessible
for attempt in range(3): for attempt in range(3):
if temp_input.exists() and os.access(temp_input, os.R_OK): if temp_input.exists() and os.access(temp_input, os.R_OK):
break break
@ -154,6 +178,29 @@ def process_folder(folder: Path, cq: int, transcode_mode: str, resolution: str,
src_width, src_height, explicit_resolution src_width, src_height, explicit_resolution
) )
# Auto-select encoder based on source bit depth if not explicitly specified
# (explicit encoder arg is passed in, so if user didn't specify, it's still the default)
# We need to check if encoder came from CLI or is the default
# For now, we'll always auto-detect and only skip if encoder was explicitly set
# Since we can't distinguish in the current flow, we'll add a parameter to track this
selected_encoder = encoder # Start with what was passed (may be default)
# Check source bit depth for auto-selection
source_bit_depth = get_source_bit_depth(temp_input)
# Auto-select encoder based on source bit depth
# 10-bit or higher (including 12-bit) → HEVC (supports up to 10-bit)
# 8-bit → AV1 (more efficient for 8-bit)
if source_bit_depth >= 10:
selected_encoder = "hevc"
encoder_note = "auto-selected (10+ bit source)"
else:
selected_encoder = "av1"
encoder_note = "auto-selected (8-bit source)"
print(f" Encoder: {selected_encoder} ({encoder_note})")
logger.info(f"Selected encoder: {selected_encoder} - Source bit depth: {source_bit_depth}-bit")
# Log resolution decision # Log resolution decision
if explicit_resolution: if explicit_resolution:
logger.info(f"Using explicitly specified resolution: {res_width}x{res_height}") logger.info(f"Using explicitly specified resolution: {res_width}x{res_height}")
@ -168,8 +215,16 @@ def process_folder(folder: Path, cq: int, transcode_mode: str, resolution: str,
print(f" Source {src_width}x{src_height} is at or below 1080p. Preserving resolution.") print(f" Source {src_width}x{src_height} is at or below 1080p. Preserving resolution.")
logger.info(f"Source {src_width}x{src_height} (<=1080p). Preserving source resolution.") logger.info(f"Source {src_width}x{src_height} (<=1080p). Preserving source resolution.")
# Set CQ based on content type and target resolution # Set CQ based on content type, target resolution, and encoder
content_cq = config["encode"]["cq"].get(f"tv_{target_resolution}" if is_tv else f"movie_{target_resolution}", 32) if is_anime:
cq_key = f"anime_{target_resolution}"
elif is_tv:
cq_key = f"tv_{target_resolution}"
else:
cq_key = f"movie_{target_resolution}"
# Look up CQ from encoder-specific section
encoder_cq_config = config["encode"]["cq"].get(selected_encoder, {})
content_cq = encoder_cq_config.get(cq_key, 32)
file_cq = cq if cq is not None else content_cq file_cq = cq if cq is not None else content_cq
# Always output as .mkv (AV1 video codec) with [EHX] suffix # Always output as .mkv (AV1 video codec) with [EHX] suffix
@ -185,9 +240,21 @@ def process_folder(folder: Path, cq: int, transcode_mode: str, resolution: str,
# Attempt encoding # Attempt encoding
try: try:
# Determine audio_filter config (CLI arg overrides config file)
# --filter-audio flag means: show interactive prompt
if filter_audio:
audio_filter_config = {"enabled": True, "interactive": True}
# If --audio-select provided, skip interactive and use pre-selected streams
if audio_select:
audio_filter_config["preselected"] = audio_select
else:
# Use config file setting (if present)
audio_filter_config = config.get("general", {}).get("audio_filter", {})
orig_size, out_size, reduction_ratio = run_ffmpeg( orig_size, out_size, reduction_ratio = run_ffmpeg(
temp_input, temp_output, file_cq, res_width, res_height, src_width, src_height, temp_input, temp_output, file_cq, res_width, res_height, src_width, src_height,
filter_flags, audio_config, method, bitrate_config, subtitle_file, audio_language filter_flags, audio_config, method, bitrate_config, selected_encoder, subtitle_file, audio_language,
audio_filter_config, test_mode
) )
# Check if encode met size target # Check if encode met size target
@ -283,7 +350,7 @@ def process_folder(folder: Path, cq: int, transcode_mode: str, resolution: str,
_save_successful_encoding( _save_successful_encoding(
file, temp_input, temp_output, orig_size, out_size, file, temp_input, temp_output, orig_size, out_size,
reduction_ratio, method, src_width, src_height, res_width, res_height, reduction_ratio, method, src_width, src_height, res_width, res_height,
file_cq, tracker_file, folder, is_tv, config, test_mode, subtitle_file file_cq, tracker_file, folder, is_tv, suffix, config, test_mode, subtitle_file
) )
# In test mode, stop after first successful file # In test mode, stop after first successful file
@ -335,8 +402,8 @@ def process_folder(folder: Path, cq: int, transcode_mode: str, resolution: str,
temp_input, temp_output, file_data['file_cq'], temp_input, temp_output, file_data['file_cq'],
file_data['res_width'], file_data['res_height'], file_data['res_width'], file_data['res_height'],
file_data['src_width'], file_data['src_height'], file_data['src_width'], file_data['src_height'],
filter_flags, audio_config, "Bitrate", bitrate_config, filter_flags, audio_config, "Bitrate", bitrate_config, selected_encoder,
file_data.get('subtitle_file'), audio_language file_data.get('subtitle_file'), audio_language, None, test_mode
) )
# Check if bitrate also failed # Check if bitrate also failed
@ -358,7 +425,7 @@ def process_folder(folder: Path, cq: int, transcode_mode: str, resolution: str,
file_data['src_width'], file_data['src_height'], file_data['src_width'], file_data['src_height'],
file_data['res_width'], file_data['res_height'], file_data['res_width'], file_data['res_height'],
file_data['file_cq'], tracker_file, file_data['file_cq'], tracker_file,
folder, file_data['is_tv'], config, False, folder, file_data['is_tv'], suffix, config, False,
file_data.get('subtitle_file') file_data.get('subtitle_file')
) )
@ -390,7 +457,7 @@ def process_folder(folder: Path, cq: int, transcode_mode: str, resolution: str,
def _save_successful_encoding(file, temp_input, temp_output, orig_size, out_size, def _save_successful_encoding(file, temp_input, temp_output, orig_size, out_size,
reduction_ratio, method, src_width, src_height, res_width, res_height, reduction_ratio, method, src_width, src_height, res_width, res_height,
file_cq, tracker_file, folder, is_tv, config=None, test_mode=False, subtitle_file=None): file_cq, tracker_file, folder, is_tv, suffix, config=None, test_mode=False, subtitle_file=None):
"""Helper function to save successfully encoded files with [EHX] tag and clean up subtitle files.""" """Helper function to save successfully encoded files with [EHX] tag and clean up subtitle files."""
# In test mode, show ratio and skip file move/cleanup # In test mode, show ratio and skip file move/cleanup
@ -411,13 +478,24 @@ def _save_successful_encoding(file, temp_input, temp_output, orig_size, out_size
logger.info(f"TEST MODE - File: {file.name} | Ratio: {percentage}% | Method: {method}") logger.info(f"TEST MODE - File: {file.name} | Ratio: {percentage}% | Method: {method}")
return return
# Check if file is in a Featurettes folder - if so, remove suffix from destination filename
folder_parts = [p.lower() for p in file.parent.parts]
is_featurette = "featurettes" in folder_parts
if is_featurette:
# Remove suffix from temp_output.name for Featurettes
output_name = temp_output.name
if suffix in output_name:
output_name = output_name.replace(suffix, "")
dest_file = file.parent / output_name
else:
dest_file = file.parent / temp_output.name dest_file = file.parent / temp_output.name
shutil.move(temp_output, dest_file) shutil.move(temp_output, dest_file)
print(f"🚚 Moved {temp_output.name}{dest_file.name}") print(f"🚚 Moved {temp_output.name}{dest_file.name}")
logger.info(f"Moved {temp_output.name}{dest_file.name}") logger.info(f"Moved {temp_output.name}{dest_file.name}")
# Classify file type based on folder # Classify file type based on folder (folder_parts already defined earlier)
folder_parts = [p.lower() for p in folder.parts]
if "tv" in folder_parts: if "tv" in folder_parts:
f_type = "tv" f_type = "tv"
tv_index = folder_parts.index("tv") tv_index = folder_parts.index("tv")
@ -462,8 +540,13 @@ def _save_successful_encoding(file, temp_input, temp_output, orig_size, out_size
try: try:
temp_input.unlink() temp_input.unlink()
# Only delete original file if NOT in Featurettes folder (Featurettes are re-encoded in place)
if not is_featurette:
file.unlink() file.unlink()
logger.info(f"Deleted original and processing copy for {file.name}") logger.info(f"Deleted original and processing copy for {file.name}")
else:
logger.info(f"Featurettes file preserved at origin: {file.name}")
# Clean up subtitle file if it was embedded # Clean up subtitle file if it was embedded
if subtitle_file and subtitle_file.exists(): if subtitle_file and subtitle_file.exists():

View File

@ -22,17 +22,56 @@ def get_source_resolution(input_file: Path) -> tuple:
"-of", "default=noprint_wrappers=1:nokey=1:noprint_wrappers=1", "-of", "default=noprint_wrappers=1:nokey=1:noprint_wrappers=1",
str(input_file) str(input_file)
] ]
result = subprocess.run(cmd, capture_output=True, text=True, check=True) result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8', errors='ignore', check=False)
if result.stdout:
lines = result.stdout.strip().split("\n") lines = result.stdout.strip().split("\n")
width = int(lines[0]) if len(lines) > 0 else 1920 width = int(lines[0]) if len(lines) > 0 and lines[0].strip() else 1920
height = int(lines[1]) if len(lines) > 1 else 1080 height = int(lines[1]) if len(lines) > 1 and lines[1].strip() else 1080
logger.info(f"Source resolution detected: {width}x{height}") logger.info(f"Source resolution detected: {width}x{height}")
return (width, height) return (width, height)
else:
logger.warning(f"ffprobe returned no output for {input_file.name}. Defaulting to 1920x1080")
return (1920, 1080)
except Exception as e: except Exception as e:
logger.warning(f"Failed to detect source resolution: {e}. Defaulting to 1920x1080") logger.warning(f"Failed to detect source resolution: {e}. Defaulting to 1920x1080")
return (1920, 1080) return (1920, 1080)
def get_source_bit_depth(input_file: Path) -> int:
"""
Detect source video bit depth (8, 10, or 12).
Returns: 12, 10, or 8 (default)
"""
try:
cmd = [
"ffprobe", "-v", "error",
"-select_streams", "v:0",
"-show_entries", "stream=pix_fmt",
"-of", "default=noprint_wrappers=1:nokey=1",
str(input_file)
]
result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8', errors='ignore', check=False)
if result.stdout:
pix_fmt = result.stdout.strip().lower()
# Check for 12-bit indicators first
if any(x in pix_fmt for x in ["12le", "12be"]):
logger.info(f"Source bit depth detected: 12-bit ({pix_fmt})")
return 12
# Check for 10-bit indicators
elif any(x in pix_fmt for x in ["10le", "10be", "p010", "yuv420p10"]):
logger.info(f"Source bit depth detected: 10-bit ({pix_fmt})")
return 10
else:
logger.info(f"Source bit depth detected: 8-bit ({pix_fmt})")
return 8
else:
logger.debug(f"Could not detect bit depth for {input_file.name}. Defaulting to 8-bit")
return 8
except Exception as e:
logger.warning(f"Failed to detect source bit depth: {e}. Defaulting to 8-bit")
return 8
def determine_target_resolution(src_width: int, src_height: int, explicit_resolution: str = None) -> tuple: def determine_target_resolution(src_width: int, src_height: int, explicit_resolution: str = None) -> tuple:
""" """
Determine target resolution based on source and explicit override. Determine target resolution based on source and explicit override.

File diff suppressed because it is too large Load Diff

View File

@ -31,3 +31,30 @@
2026-01-01 01:25:05 | Supernatural - S07E17 - The Born-Again Identity x265 AC3 Bluray-1080p HiQVE.mkv | CQ error: Command '['ffmpeg', '-y', '-i', 'C:\\Users\\Tyler\\Documents\\GitHub\\conversion_project\\processing 2026-01-01 01:25:05 | Supernatural - S07E17 - The Born-Again Identity x265 AC3 Bluray-1080p HiQVE.mkv | CQ error: Command '['ffmpeg', '-y', '-i', 'C:\\Users\\Tyler\\Documents\\GitHub\\conversion_project\\processing
2026-01-01 13:17:15 | The Office (US) - S08E04 - Garden Party x265 AAC Bluray-1080p Silence.mkv | CQ failed: Size threshold not met (122.2%) 2026-01-01 13:17:15 | The Office (US) - S08E04 - Garden Party x265 AAC Bluray-1080p Silence.mkv | CQ failed: Size threshold not met (122.2%)
2026-01-01 13:22:48 | The Office (US) - S08E04 - Garden Party x265 AAC Bluray-1080p Silence.mkv | CQ failed: Size threshold not met (101.3%) 2026-01-01 13:22:48 | The Office (US) - S08E04 - Garden Party x265 AAC Bluray-1080p Silence.mkv | CQ failed: Size threshold not met (101.3%)
2026-01-01 20:53:58 | ._Superman (2025) x265 EAC3 7.1 Bluray-1080p Ghost.mkv | CQ error: Command '['ffmpeg', '-y', '-i', 'C:\\Users\\Tyler\\Documents\\GitHub\\conversion_project\\processing
2026-01-01 20:55:56 | [sam] Vanitas no Carte - 02 [BD 1080p FLAC] [8822B4BC].mkv | Unexpected error: too many values to unpack (expected 5)
2026-01-01 20:56:12 | [sam] Vanitas no Carte - 03 [BD 1080p FLAC] [BDE63D2B].mkv | Unexpected error: too many values to unpack (expected 5)
2026-01-01 21:00:10 | [sam] Vanitas no Carte - 02 [BD 1080p FLAC] [8822B4BC].mkv | Unexpected error: too many values to unpack (expected 5)
2026-01-01 22:51:03 | ._Superman (2025) x265 EAC3 7.1 Bluray-1080p Ghost.mkv | CQ error: Command '['ffmpeg', '-y', '-i', 'C:\\Users\\Tyler\\Documents\\GitHub\\conversion_project\\processing
2026-01-01 22:51:21 | A New Era DC Takes Off.mkv | CQ failed: Size threshold not met (81.8%)
2026-01-01 22:51:37 | Adventures in the Making of “Superman”.mkv | Unexpected error: 'NoneType' object has no attribute 'split'
2026-01-01 22:53:54 | ._Superman (2025) x265 EAC3 7.1 Bluray-1080p Ghost.mkv | CQ error: Command '['ffmpeg', '-y', '-i', 'C:\\Users\\Tyler\\Documents\\GitHub\\conversion_project\\processing
2026-01-01 22:54:13 | A New Era DC Takes Off.mkv | CQ failed: Size threshold not met (81.8%)
2026-01-01 22:57:40 | ._Superman (2025) x265 EAC3 7.1 Bluray-1080p Ghost.mkv | CQ error: Command '['ffmpeg', '-y', '-i', 'C:\\Users\\Tyler\\Documents\\GitHub\\conversion_project\\processing
2026-01-01 22:58:01 | A New Era DC Takes Off.mkv | Unexpected error: name 'suffix' is not defined
2026-01-08 10:37:48 | According to Plan.mkv | CQ failed: Size threshold not met (94.3%)
2026-01-08 10:37:56 | Bloopers of the Caribbean.mkv | CQ failed: Size threshold not met (122.9%)
2026-01-08 10:39:01 | Captain Jack - From Head to Toe.mkv | CQ failed: Size threshold not met (110.9%)
2026-01-08 11:46:19 | Anatomy of a Scene - The Maelstrom.mkv | CQ failed: Size threshold not met (202.4%)
2026-01-08 11:46:59 | Bloopers of the Caribbean.mkv | CQ failed: Size threshold not met (107.0%)
2026-01-08 11:50:26 | Deleted & Extended Scenes.mkv | CQ failed: Size threshold not met (116.3%)
2026-01-08 13:38:12 | An Epic at Sea.mkv | CQ failed: Size threshold not met (85.1%)
2026-01-08 13:38:27 | Becoming Barbossa.mkv | CQ failed: Size threshold not met (93.0%)
2026-01-08 13:38:47 | Becoming Captain Jack.mkv | CQ failed: Size threshold not met (95.2%)
2026-01-08 13:56:34 | Bloopers of the Caribbean.mkv | CQ failed: Size threshold not met (103.5%)
2026-01-08 14:04:01 | Dead Men Tell No Tales - The Making of a New Adventure.mkv | CQ failed: Size threshold not met (156.6%)
2026-01-08 14:22:53 | Bloopers of the Caribbean.mkv | CQ failed: Size threshold not met (116.2%)
2026-01-08 14:24:16 | Deleted and Extended Scenes.mkv | CQ failed: Size threshold not met (115.3%)
2026-01-08 14:26:55 | Easter Eggs.mkv | CQ failed: Size threshold not met (227.9%)
2026-01-08 16:15:19 | Trailer [kr].mkv | CQ failed: Size threshold not met (106.3%)
2026-01-08 18:49:04 | The Ultimate Villain.mkv | CQ failed: Size threshold not met (85.9%)

23
main.py
View File

@ -89,10 +89,10 @@ def main():
formatter_class=argparse.RawDescriptionHelpFormatter, formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=""" epilog="""
Examples: Examples:
%(prog)s "C:\\Videos\\Movies" # Smart mode (preserve resolution, 4K->1080p) %(prog)s "C:\\Videos\\Movies" # CQ mode (preserve resolution, 4K->1080p)
%(prog)s "C:\\Videos\\TV" --r 720 --m bitrate # Force 720p, bitrate mode %(prog)s "C:\\Videos\\TV" --r 720 --m bitrate # Force 720p, bitrate mode
%(prog)s "C:\\Videos\\Anime" --cq 28 --r 1080 # Force 1080p, CQ=28 %(prog)s "C:\\Videos\\Anime" --cq 28 --r 1080 # Force 1080p, CQ=28
%(prog)s "C:\\Videos\\Low-Res" --r 480 # Force 480p for low-res content %(prog)s "C:\\Videos\\Low-Res" --m compression --r 480 # Force 480p, try CQ then bitrate for compression
""" """
) )
@ -100,8 +100,13 @@ Examples:
parser.add_argument("--cq", type=int, help="Override default CQ value") parser.add_argument("--cq", type=int, help="Override default CQ value")
parser.add_argument( parser.add_argument(
"--m", "--mode", dest="transcode_mode", default="cq", "--m", "--mode", dest="transcode_mode", default="cq",
choices=["cq", "bitrate"], choices=["cq", "bitrate", "compression"],
help="Encode mode: CQ (constant quality) or Bitrate mode" help="Encode mode: 'cq' (constant quality only), 'bitrate' (bitrate only), or 'compression' (try CQ then bitrate fallback)"
)
parser.add_argument(
"--encoder", dest="encoder", default="hevc",
choices=["hevc", "av1"],
help="Video encoder: 'hevc' for HEVC NVENC 10-bit (default), 'av1' for AV1 NVENC 8-bit"
) )
parser.add_argument( parser.add_argument(
"--r", "--resolution", dest="resolution", default=None, "--r", "--resolution", dest="resolution", default=None,
@ -116,6 +121,14 @@ Examples:
"--language", dest="audio_language", default=None, "--language", dest="audio_language", default=None,
help="Tag audio streams with language code (e.g., eng, spa, fra). If not set, audio language is unchanged" help="Tag audio streams with language code (e.g., eng, spa, fra). If not set, audio language is unchanged"
) )
parser.add_argument(
"--filter-audio", dest="filter_audio", default=None, action="store_true",
help="Interactive audio selection: show audio streams and let user choose which to keep (overrides config setting)"
)
parser.add_argument(
"--audio-select", dest="audio_select", default=None,
help="Pre-select audio streams to keep (comma-separated, e.g., 1,2). Skips interactive prompt. Requires --filter-audio"
)
args = parser.parse_args() args = parser.parse_args()
# Load configuration # Load configuration
@ -132,7 +145,7 @@ Examples:
return return
# Process folder # Process folder
process_folder(folder, args.cq, args.transcode_mode, args.resolution, config, TRACKER_FILE, args.test_mode, args.audio_language) process_folder(folder, args.cq, args.transcode_mode, args.resolution, config, TRACKER_FILE, args.test_mode, args.audio_language, args.filter_audio, args.audio_select, args.encoder)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -0,0 +1,322 @@
{
"P:\\tv\\Hero Inside (2023)": 7372329680,
"P:\\tv\\Below Deck": 47516712212,
"P:\\tv\\The Penguin": 4459075060,
"P:\\tv\\Banshee (2013)": 25030541772,
"P:\\tv\\Dungeons & Dragons": 6660128393,
"P:\\tv\\Made For Love (2021)": 2211136772,
"P:\\tv\\Sirens (2025)": 4246622090,
"P:\\tv\\Landman (2024)": 35220290035,
"P:\\tv\\Last Man Standing": 49393251846,
"P:\\tv\\Alien - Earth (2025)": 2926145405,
"P:\\tv\\The Big Door Prize": 2314902686,
"P:\\tv\\Government Cheese (2025)": 15970704500,
"P:\\tv\\In the Dark (2019)": 2555891397,
"P:\\tv\\Tulsa King": 41351406080,
"P:\\tv\\Dopesick": 2571994785,
"P:\\tv\\Taylor (2025)": 2621206209,
"P:\\tv\\Star Trek Lower Decks": 33090597113,
"P:\\tv\\Face Off (2011)": 83155672195,
"P:\\tv\\Catch-22": 7113496871,
"P:\\tv\\Canada's Drag Race": 103586850759,
"P:\\tv\\Over the Garden Wall": 2937573633,
"P:\\tv\\The Traitors (US) (2023)": 48149750078,
"P:\\tv\\1923": 22125507023,
"P:\\tv\\Loki": 20082144632,
"P:\\tv\\House of the Dragon": 23959073249,
"P:\\tv\\The Trunk (2024)": 16810949304,
"P:\\tv\\The Chosen (2019)": 54241850899,
"P:\\tv\\Lucky Hank": 7336222432,
"P:\\tv\\Station Eleven": 2708694925,
"P:\\tv\\Kitchen Nightmares UK": 11563663098,
"P:\\tv\\The Good Lord Bird (2020)": 5619421375,
"P:\\tv\\Wolf Pack": 6844099384,
"P:\\tv\\Below Deck Mediterranean": 39902249615,
"P:\\tv\\The Old Man (2022)": 26139845941,
"P:\\tv\\Schitt's Creek": 9325109901,
"P:\\tv\\Mr. & Mrs. Smith (2024)": 5316681916,
"P:\\tv\\Percy Jackson and the Olympians": 3558450335,
"P:\\tv\\Firefly (2002)": 7517428895,
"P:\\tv\\Ballers": 13002096756,
"P:\\tv\\Bupkis": 13034439710,
"P:\\tv\\The Offer": 9070667475,
"P:\\tv\\Life After People (2009)": 45628647899,
"P:\\tv\\The Lord of the Rings - The Rings of Power": 12834498889,
"P:\\tv\\Paradise (2025)": 8024209737,
"P:\\tv\\Nobody Wants This": 11516933757,
"P:\\tv\\Shrinking (2023)": 17293593983,
"P:\\tv\\Hawkeye": 13524278345,
"P:\\tv\\Home Economics": 14315967074,
"P:\\tv\\Time Bandits (2024)": 6997478287,
"P:\\tv\\Lessons in Chemistry (2023)": 5485801173,
"P:\\tv\\1883": 4514294832,
"P:\\tv\\Love, Death & Robots (2019)": 8204860116,
"P:\\tv\\The Legend of Vox Machina": 25197294503,
"P:\\tv\\Harry Potter - Wizards of Baking (2024)": 23545641052,
"P:\\tv\\The Bachelor": 40368931577,
"P:\\tv\\American Horror Story": 142468660014,
"P:\\tv\\Yellowstone (2018)": 89724605866,
"P:\\tv\\St. Denis Medical (2024)": 18704263469,
"P:\\tv\\Cobra Kai": 39761471967,
"P:\\tv\\Power (2014)": 20414619656,
"P:\\tv\\The Originals (2013)": 72912846985,
"P:\\tv\\The Edge of Sleep": 1358235145,
"P:\\tv\\3 Body Problem": 11369334730,
"P:\\tv\\New Girl": 40676856398,
"P:\\tv\\Assembly Required (2021)": 5737519036,
"P:\\tv\\30 Rock (2006)": 81412969909,
"P:\\tv\\Rupauls Drag Race UK vs The World": 35504142221,
"P:\\tv\\Daredevil - Born Again (2025)": 7647367391,
"P:\\tv\\Brooklyn Nine Nine": 45722673163,
"P:\\tv\\Taskmaster - Champion of Champions": 2700754514,
"P:\\tv\\Kim's Convenience": 30475634673,
"P:\\tv\\The Office (US)": 161867626607,
"P:\\tv\\Stranger Things (2016)": 66712664909,
"P:\\tv\\Rupaul's Drag Race Vegas Revue": 2532474468,
"P:\\tv\\The Umbrella Academy": 55348092191,
"P:\\tv\\Secret Celebrity RuPaul's Drag Race": 4211193920,
"P:\\tv\\Andor (2022)": 25679584728,
"P:\\tv\\The Bondsman (2025)": 3112664353,
"P:\\tv\\Ghosts (2021)": 4574333812,
"P:\\tv\\Interior Chinatown": 3167640001,
"P:\\tv\\Selfie": 5013734266,
"P:\\tv\\Supernatural": 209274293691,
"P:\\tv\\Superman and Lois": 44881535930,
"P:\\tv\\Black Sails (2014)": 11356486450,
"P:\\tv\\Taskmaster (CA) (2022)": 2431664380,
"P:\\tv\\The Last of Us": 30545352719,
"P:\\tv\\Halo": 6961206915,
"P:\\tv\\Home Improvement 1991": 48878774505,
"P:\\tv\\Detroiters (2017)": 33750584701,
"P:\\tv\\Wildemount Wildlings (2025)": 3348907992,
"P:\\tv\\Terminator Zero": 3384699699,
"P:\\tv\\Um, Actually": 12360993522,
"P:\\tv\\The Rain (2018)": 2941174698,
"P:\\tv\\Harley Quinn": 20857796821,
"P:\\tv\\Lawmen - Bass Reeves (2023)": 5363156538,
"P:\\tv\\Parks and Recreation": 37277190974,
"P:\\tv\\Mythic Quest": 16965795814,
"P:\\tv\\Invincible (2021)": 19742824176,
"P:\\tv\\The Bear (2022)": 43665628138,
"P:\\tv\\Jentry Chau vs. the Underworld (2024)": 1406237358,
"P:\\tv\\Countdown (2025)": 8935252687,
"P:\\tv\\The Great British Bake Off": 78,
"P:\\tv\\Smartypants": 15959708127,
"P:\\tv\\Scenes from a Marriage (US)": 12493986505,
"P:\\tv\\The Franchise (2024)": 2981270395,
"P:\\tv\\Chad Powers (2025)": 2474659236,
"P:\\tv\\Doctor Who (2005)": 5820708419,
"P:\\tv\\Bad Monkey": 7767595411,
"P:\\tv\\Swimming with Sharks": 4426141798,
"P:\\tv\\English Teacher": 7603165476,
"P:\\tv\\Resident Alien (2021)": 17522605407,
"P:\\tv\\Krypton (2018)": 10875524680,
"P:\\tv\\Vikings (2013)": 194095449878,
"P:\\tv\\Arcane (2021)": 19588567847,
"P:\\tv\\Ludwig (2024)": 2670615425,
"P:\\tv\\Canada's Drag Race vs The World": 7844155647,
"P:\\tv\\BattleBots (2015)": 69,
"P:\\tv\\Abbott Elementary (2021)": 24595462535,
"P:\\tv\\Billy the Kid": 44803721006,
"P:\\tv\\Quantum Leap 2022": 8902776416,
"P:\\tv\\Obi-Wan Kenobi": 13867986923,
"P:\\tv\\Matlock (2024)": 34470939613,
"P:\\tv\\The Fall of Diddy (2025)": 2431035593,
"P:\\tv\\Kaos": 5164057710,
"P:\\tv\\Shifting Gears (2025)": 12649531141,
"P:\\tv\\Saving Hope": 33116225358,
"P:\\tv\\Gen V (2023)": 16871757804,
"P:\\tv\\Below Deck Sailing Yacht": 12706704039,
"P:\\tv\\Monarch Legacy of Monsters": 18371826949,
"P:\\tv\\High Potential": 24339309461,
"P:\\tv\\Band of Brothers (2001)": 15129362120,
"P:\\tv\\Quantum Leap (1989)": 39284023472,
"P:\\tv\\Harley and the Davidsons": 76,
"P:\\tv\\Rupaul's Drag Race All Stars": 60579323023,
"P:\\tv\\Amazing Stories (2020)": 4281304451,
"P:\\tv\\Murder She Wrote": 12095973826,
"P:\\tv\\Kitchen Nightmares US": 56092851597,
"P:\\tv\\Game Changer": 38317757866,
"P:\\tv\\Taskmaster AU": 20527610746,
"P:\\tv\\Fallout": 19686023936,
"P:\\tv\\Young Sheldon": 21714069112,
"P:\\tv\\Vice Principals (2016)": 18406955713,
"P:\\tv\\Adventuring Academy": 62196997373,
"P:\\tv\\Solar Opposites": 1138214210,
"P:\\tv\\Pok\u00e9mon Concierge (2023)": 1134616527,
"P:\\tv\\Better Call Saul": 31152560439,
"P:\\tv\\Counterpart": 4875616955,
"P:\\tv\\The Paper (2025)": 8102218176,
"P:\\tv\\Chuck": 32193192829,
"P:\\tv\\The Bachelorette": 9927266246,
"P:\\tv\\Wandavision": 10099450034,
"P:\\tv\\Pantheon": 13397374449,
"P:\\tv\\The Gilded Age": 90505242840,
"P:\\tv\\Gastronauts": 9365810750,
"P:\\tv\\American Gods (2017)": 43921706762,
"P:\\tv\\The IT Crowd (2006)": 9239572772,
"P:\\tv\\Winning Time - The Rise of the Lakers Dynasty (2022)": 37911197652,
"P:\\tv\\Monet's Slumber Party": 8253206091,
"P:\\tv\\Walker": 5492500161,
"P:\\tv\\Stargirl": 9507100884,
"P:\\tv\\House of Guinness (2025)": 5444928896,
"P:\\tv\\Father Brown": 18896564477,
"P:\\tv\\Silo (2023)": 12897630564,
"P:\\tv\\Your Honor (2020)": 25879839349,
"P:\\tv\\Welcome to Wrexham": 66664948104,
"P:\\tv\\Royal Pains (2009)": 1247586112,
"P:\\tv\\The Continental (2023)": 1920206807,
"P:\\tv\\Citadel": 2339699246,
"P:\\tv\\The 10th Kingdom (2000)": 14174589505,
"P:\\tv\\Parlor Room": 12022280605,
"P:\\tv\\Its Always Sunny in Philadelphia": 84650830434,
"P:\\tv\\Star Wars - Skeleton Crew (2024)": 2940779001,
"P:\\tv\\Rupaul's Drag Race": 57149739065,
"P:\\tv\\Only Murders in the Building (2021)": 2379838148,
"P:\\tv\\Running Man": 10279755878,
"P:\\tv\\Shetland": 18537045340,
"P:\\tv\\Adults (2025)": 6845585714,
"P:\\tv\\iCarly (2021)": 19966043984,
"P:\\tv\\Villainous (2017)": 1961793524,
"P:\\tv\\The Terminal List - Dark Wolf (2025)": 9939046560,
"P:\\tv\\Ted Lasso (2020)": 52046307136,
"P:\\tv\\Murderbot (2025)": 18338040970,
"P:\\tv\\RuPaul's Drag Race Down Under": 27454793482,
"P:\\tv\\Gravity Falls": 31900305156,
"P:\\tv\\The Santa Clauses (2022)": 6400385164,
"P:\\tv\\Marvel's The Punisher (2017)": 32242478897,
"P:\\tv\\Dracula (2020)": 2147285239,
"P:\\tv\\Extraordinary": 6934203888,
"P:\\tv\\Cyberpunk - Edgerunners (2022)": 11313875182,
"P:\\tv\\Rick and Morty": 31672318625,
"P:\\tv\\Welcome to Chippendales (2022)": 10423545837,
"P:\\tv\\Squid Game (2021)": 22082475135,
"P:\\tv\\MobLand (2025)": 6622179548,
"P:\\tv\\Taskmaster (NZ)": 71323320898,
"P:\\tv\\The Newsroom": 27756667258,
"P:\\tv\\The Pretender": 18425629462,
"P:\\tv\\Hazbin Hotel (2024)": 10906489515,
"P:\\tv\\Raised by wolves": 9720677524,
"P:\\tv\\Tomb Raider - The Legend of Lara Croft": 9341088252,
"P:\\tv\\Spartacus": 75639017886,
"P:\\tv\\Worst Cooks in America (2010)": 22063867049,
"P:\\tv\\Avenue 5": 12572813494,
"P:\\tv\\Man Down (2013)": 5077144151,
"P:\\tv\\Outlander": 27364180668,
"P:\\tv\\The Eternaut": 17178505929,
"P:\\tv\\Below Deck Down Under (2022)": 36006759742,
"P:\\tv\\Dirty Laundry": 27626331672,
"P:\\tv\\Chilling Adventures of Sabrina (2018)": 23147355371,
"P:\\tv\\The Studio (2025)": 11530554023,
"P:\\tv\\The Forsytes (2025)": 4034792830,
"P:\\tv\\Platonic (2023)": 17488146510,
"P:\\tv\\Love Island (US) (2019)": 20699120877,
"P:\\tv\\Dark Side of the Ring": 11863132534,
"P:\\tv\\The Day of the Jackal (2024)": 17787097381,
"P:\\tv\\Utopia (AU)": 8691287022,
"P:\\tv\\Sweetpea": 2706241673,
"P:\\tv\\Dateline NBC (1992)": 19267231607,
"P:\\tv\\Euphoria": 40925172559,
"P:\\tv\\The Consultant (2023)": 74,
"P:\\tv\\Titans (2018)": 31986198137,
"P:\\tv\\Taskmaster": 142193364333,
"P:\\tv\\Ink Master": 23329086486,
"P:\\tv\\Dimension 20": 557729281243,
"P:\\tv\\Continuum (2012)": 29352883496,
"P:\\tv\\South Park": 70261225261,
"P:\\tv\\Letterkenny": 63,
"P:\\tv\\Ghosts (2019)": 40703143881,
"P:\\tv\\Moon Knight": 10976093361,
"P:\\tv\\Twisted Metal (2023)": 12547412897,
"P:\\tv\\Extrapolations": 6690715385,
"P:\\tv\\Quiet On Set - The Dark Side Of Kids TV": 12191520028,
"P:\\tv\\Sh\u014dgun": 20899988683,
"P:\\tv\\Taboo (2017)": 19309841226,
"P:\\tv\\Ironheart (2025)": 3153557870,
"P:\\tv\\DOTA - Dragon's Blood (2021)": 12538510766,
"P:\\tv\\Knuckles": 2140786440,
"P:\\tv\\Shoresy": 9900029120,
"P:\\tv\\Impractical Jokers": 13357380400,
"P:\\tv\\One More Time (2024)": 6434473461,
"P:\\tv\\Crowd Control": 9644641207,
"P:\\tv\\Dimension 20's Adventuring Party": 12002285238,
"P:\\tv\\Special Ops Lioness": 9765393961,
"P:\\tv\\Ted (2024)": 3024624414,
"P:\\tv\\Mighty Nein (2025)": 6138965943,
"P:\\tv\\Citadel - Diana": 13304679453,
"P:\\tv\\Our Flag Means Death": 2107045664,
"P:\\tv\\Make Some Noise": 25555591381,
"P:\\tv\\Mayor of Kingstown (2021)": 65464041666,
"P:\\tv\\The Take": 6020370013,
"P:\\tv\\Agatha All Along": 3411637969,
"P:\\tv\\The Amazing Digital Circus (2023)": 4739070191,
"P:\\tv\\The Now": 836886747,
"P:\\tv\\Poppa\u2019s House": 13794748297,
"P:\\tv\\Married at First Sight (2014)": 30275711911,
"P:\\tv\\The Closer": 47449608535,
"P:\\tv\\Junior Taskmaster (2024)": 4133620030,
"P:\\tv\\WondLa": 1399628000,
"P:\\tv\\The Second Best Hospital in the Galaxy (2024)": 3636394169,
"P:\\tv\\Being Human (2011)": 66311454464,
"P:\\tv\\SCORPION": 54081802764,
"P:\\tv\\The Goes Wrong Show (2019)": 3676343887,
"P:\\tv\\See": 12316511887,
"P:\\tv\\Dirk Gently's Holistic Detective Agency (2016)": 11935610182,
"P:\\tv\\Tokyo Override (2024)": 3802255332,
"P:\\tv\\Peacemaker (2022)": 13199970800,
"P:\\tv\\The Falcon and The Winter Soldier (2021)": 11657055937,
"P:\\tv\\Fargo (2014)": 93247402537,
"P:\\tv\\Killer Cakes": 3673781461,
"P:\\tv\\The Mandalorian": 36487773789,
"P:\\tv\\Very Important People": 12237876110,
"P:\\tv\\Smiling Friends": 5633340834,
"P:\\tv\\Game Changers (2024)": 5880504271,
"P:\\tv\\Star Strek Strange New Worlds": 13781151928,
"P:\\tv\\Galavant": 12147863291,
"P:\\tv\\She-Hulk Attorney at Law": 10233633417,
"P:\\tv\\From Dusk Till Dawn - The Series (2014)": 5360771338,
"P:\\tv\\The Journal of the Mysterious Creatures (2019)": 92,
"P:\\tv\\Fallen (2024)": 4161867429,
"P:\\tv\\Severance": 15044806873,
"P:\\tv\\The Great (2020)": 22361386693,
"P:\\tv\\What If": 21312022582,
"P:\\tv\\Rupaul's Drag Race UK": 110914388896,
"P:\\tv\\Game Of Thrones": 119681469870,
"P:\\tv\\Belgravia - The Next Chapter": 8340040939,
"P:\\tv\\Hitmen (2020)": 12274410846,
"P:\\tv\\Haunted Hotel (2025)": 4735071992,
"P:\\tv\\The Book of Boba Fett": 12039417291,
"P:\\tv\\SAS Rogue Heroes (2022)": 10733559643,
"P:\\tv\\Dwight in Shining Armor": 75,
"P:\\tv\\Jury Duty": 8010062372,
"P:\\tv\\Son of Zorn (2016)": 6780978712,
"P:\\tv\\The Gentlemen (2024)": 5224500371,
"P:\\tv\\Schmigadoon!": 6206632733,
"P:\\tv\\The Drew Carey Show (1995)": 70,
"P:\\tv\\Fired on Mars (2023)": 3590992124,
"P:\\tv\\Black Bird (2022)": 5893929480,
"P:\\tv\\Billions": 31141419259,
"P:\\tv\\Reacher (2022)": 17521873037,
"P:\\tv\\The Morning Show": 94311701751,
"P:\\tv\\Secret Level": 2810124465,
"P:\\tv\\The Boys": 68010010167,
"P:\\tv\\Gordon Ramsay's Food Stars (2023)": 6344621632,
"P:\\tv\\Death and Other Details": 17844763765,
"P:\\tv\\Modern Family": 82788065200,
"P:\\tv\\Married... with Children (1987)": 64228823786,
"P:\\tv\\BattleBots": 61,
"P:\\tv\\Silicon Valley (2014)": 63657428121,
"P:\\tv\\Tires (2024)": 5375794389,
"P:\\tv\\Creature Commandos (2024)": 2331424358,
"P:\\tv\\Goosebumps (2023)": 8257419062,
"P:\\tv\\The Fall of the House of Usher (2023)": 16454192941,
"P:\\tv\\Passion for punchlines": 75514795,
"P:\\tv\\The Queen's Gambit": 4100494817,
"P:\\tv\\Suits LA (2025)": 22274831381,
"P:\\tv\\Dune - Prophecy": 3330003290,
"P:\\tv\\Unstable": 5444623642,
"P:\\tv\\The Split": 7970767632,
"P:\\tv\\Barry": 31934844666,
"P:\\tv\\The Dragon Dentist": 11317084093,
"P:\\tv\\Kevin Can F-k Himself": 11614889793
}

50
path_manager/config.xml Normal file
View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<config>
<general>
<processing_folder>processing</processing_folder>
<suffix> -EHX</suffix>
<extensions>.mkv,.mp4</extensions>
<ignore_tags>ehx,megusta</ignore_tags>
<reduction_ratio_threshold>0.5</reduction_ratio_threshold>
</general>
<path_mappings>
<map from="P:\tv" to="/mnt/plex/tv" />
<map from="P:\anime" to="/mnt/plex/anime" />
</path_mappings>
<encode>
<encoder default="nvenc">
<av1_nvenc preset="p7" bit_depth="8" pix_fmt="yuv420p" />
<hevc_nvenc preset="slow" bit_depth="10" pix_fmt="yuv420p10le" />
</encoder>
<cq>
<tv_1080>28</tv_1080>
<tv_720>32</tv_720>
<movie_1080>32</movie_1080>
<movie_720>34</movie_720>
</cq>
<fallback>
<bitrate_1080>1500k</bitrate_1080>
<maxrate_1080>1750k</maxrate_1080>
<bufsize_1080>2750k</bufsize_1080>
<bitrate_720>900k</bitrate_720>
<maxrate_720>1250k</maxrate_720>
<bufsize_720>1800k</bufsize_720>
</fallback>
<filters>
<default>lanczos</default>
<tv>bicubic</tv>
</filters>
</encode>
<audio>
<stereo>
<low>64000</low>
<medium>128000</medium>
<high>160000</high>
</stereo>
<multi_channel>
<low>384000</low>
<medium>512000</medium>
<high>640000</high>
</multi_channel>
</audio>
</config>

View File

@ -5,9 +5,14 @@ GUI Path Manager for Batch Video Transcoder
Allows easy browsing of folders and appending to paths.txt with encoding options. Allows easy browsing of folders and appending to paths.txt with encoding options.
""" """
import sys
from pathlib import Path
# Add parent directory to path so we can import core modules
sys.path.insert(0, str(Path(__file__).parent.parent))
import tkinter as tk import tkinter as tk
from tkinter import ttk, messagebox, filedialog from tkinter import ttk, messagebox, filedialog
from pathlib import Path
import os import os
import subprocess import subprocess
import re import re
@ -15,7 +20,7 @@ import json
from core.config_helper import load_config_xml from core.config_helper import load_config_xml
from core.logger_helper import setup_logger from core.logger_helper import setup_logger
logger = setup_logger(Path(__file__).parent / "logs") logger = setup_logger(Path(__file__).parent.parent / "logs")
class PathManagerGUI: class PathManagerGUI:
def __init__(self, root): def __init__(self, root):
@ -23,14 +28,16 @@ class PathManagerGUI:
self.root.title("Batch Transcoder - Path Manager") self.root.title("Batch Transcoder - Path Manager")
self.root.geometry("1100x700") self.root.geometry("1100x700")
# Load config # Load config (from parent directory)
config_path = Path(__file__).parent / "config.xml" config_path = Path(__file__).parent.parent / "config.xml"
self.config = load_config_xml(config_path) self.config = load_config_xml(config_path)
self.path_mappings = self.config.get("path_mappings", {}) # Convert path_mappings from list to dict for easier lookup
path_mappings_list = self.config.get("path_mappings", [])
self.path_mappings = {m["from"]: m["to"] for m in path_mappings_list} if isinstance(path_mappings_list, list) else path_mappings_list
# Paths file # Paths file (in root directory)
self.paths_file = Path(__file__).parent / "paths.txt" self.paths_file = Path(__file__).parent.parent / "paths.txt"
self.transcode_bat = Path(__file__).parent / "transcode.bat" self.transcode_bat = Path(__file__).parent.parent / "transcode.bat"
# Current selected folder # Current selected folder
self.selected_folder = None self.selected_folder = None
@ -40,7 +47,7 @@ class PathManagerGUI:
self.added_folders = set() # Folders that are in paths.txt self.added_folders = set() # Folders that are in paths.txt
# Cache for folder data - split per category # Cache for folder data - split per category
self.cache_dir = Path(__file__).parent / ".cache" self.cache_dir = Path(__file__).parent.parent / ".cache"
self.cache_dir.mkdir(exist_ok=True) self.cache_dir.mkdir(exist_ok=True)
self.folder_cache = {} # Only current category in memory: {folder_path: size} self.folder_cache = {} # Only current category in memory: {folder_path: size}
self.scan_in_progress = False self.scan_in_progress = False

View File

@ -0,0 +1,3 @@
{"timestamp": "2026-01-02T03:44:59Z", "level": "INFO", "message": "No cache file for tv", "module": "gui_path_manager", "funcName": "_load_cache", "line": 213}
{"timestamp": "2026-01-02T03:45:35Z", "level": "INFO", "message": "No cache file for tv", "module": "gui_path_manager", "funcName": "_load_cache", "line": 215}
{"timestamp": "2026-01-02T03:45:43Z", "level": "INFO", "message": "No cache file for movies", "module": "gui_path_manager", "funcName": "_load_cache", "line": 215}

View File

@ -1,11 +1,4 @@
--m cq "P:\movies\Starship Troopers (1997)" --m cq --cq 28 "P:\movies\Pirates of the Caribbean - At World's End (2007)"
--m cq "P:\movies\John Wick - Chapter 3 - Parabellum (2019)" --m cq --cq 28 "P:\movies\Pirates of the Caribbean - The Curse of the Black Pearl (2003)"
--m cq "P:\movies\John Wick - Chapter 2 (2017)" --m cq --cq 28 "P:\movies\Pirates of the Caribbean - Dead Men Tell No Tales (2017)"
--m cq "P:\movies\Belle (2021)" --m cq --cq 28 "P:\movies\Pirates of the Caribbean - On Stranger Tides (2011)"
--m cq "P:\movies\TAYLOR SWIFT THE ERAS TOUR (2023)"
--m cq "P:\movies\Ferris Bueller's Day Off (1986)"
--m cq --r 720 "P:\movies\The Baker (2023)"
--m cq "P:\movies\The Losers (2010)"
--m cq "P:\movies\Violent Night (2022)"
--m cq "P:\movies\Scott Pilgrim vs. the World (2010)"
--m cq "P:\movies\Small Soldiers (1998)"

9
paths.txt Normal file
View File

@ -0,0 +1,9 @@
"P:\movies\Wolf Children (2012)"
"P:\movies\The Intern (2015)"
"P:\movies\The Suicide Squad (2021)"
"P:\movies\Venom - The Last Dance (2024)"
--r 720 "P:\movies\Meet the Fockers (2004)"
--r 720 "P:\movies\Meet the Parents (2000)"
--r 720 "P:\movies\Little Fockers (2010)"
--r 1080 "P:\movies\Premium Rush (2012)"
"P:\movies\Captain America - Brave New World (2025)"