a lot of changes

This commit is contained in:
TylerCG 2025-12-31 15:40:37 -05:00
parent 0d415c7e49
commit a2e2ee45f5
22 changed files with 9910 additions and 721 deletions

406
.cache/.cache_anime.json Normal file
View File

@ -0,0 +1,406 @@
{
"P:\\anime\\The Greatest Demon Lord Is Reborn as a Typical Nobody": 5968497118,
"P:\\anime\\So, I Can't Play H!": 2528562389,
"P:\\anime\\The Ossan Newbie Adventurer, Trained to Death by the Most Powerful Party, Became Invincible": 4634410199,
"P:\\anime\\Kaifuku Jutsushi no Yarinaoshi": 3384572882,
"P:\\anime\\Gleipnir": 6112213174,
"P:\\anime\\Banished from the Hero's Party, I Decided to Live a Quiet Life in the Countryside (2021)": 10648931598,
"P:\\anime\\I'm Standing on a Million Lives": 8789518857,
"P:\\anime\\Lv1 Maou to One Room Yuusha": 2352794776,
"P:\\anime\\The 8th son! Are you kidding me! (2020)": 2984369205,
"P:\\anime\\Good Bye, Dragon Life (2024)": 3039400658,
"P:\\anime\\Shinchou Yuusha": 7936747223,
"P:\\anime\\Battle Game in 5 Seconds": 5594736882,
"P:\\anime\\Infinite Dendrogram": 13754309450,
"P:\\anime\\Magical Senpai": 3401214483,
"P:\\anime\\Noumin Kanren no Skill bakka Agetetara Nazeka Tsuyoku Natta": 3858206539,
"P:\\anime\\No Game No Life": 2517488069,
"P:\\anime\\Keikenzumi na Kimi to": 8117430342,
"P:\\anime\\Broken Blade": 3859920179,
"P:\\anime\\Gachiakuta (2025)": 10697119170,
"P:\\anime\\Princess Connect": 12937047999,
"P:\\anime\\ReLife": 1565479993,
"P:\\anime\\Shinobi no Ittoki (2022)": 3896251319,
"P:\\anime\\World Break - Aria of Curse for a Holy Swordsman (2015)": 5244615290,
"P:\\anime\\Chillin' in Another World with Level 2 Super Cheat Powers": 4646756870,
"P:\\anime\\Vinland Saga (2019)": 4723680531,
"P:\\anime\\Bucchigiri!!": 3895924967,
"P:\\anime\\Kyoukai Senki": 6697938339,
"P:\\anime\\Shachou Battle No Jikan Desu": 7376322052,
"P:\\anime\\The Foolish Angel Dances With the Devil": 4048585416,
"P:\\anime\\Bokutachi no Remake": 1499113600,
"P:\\anime\\Handyman Saitou in Another World": 3817097529,
"P:\\anime\\Welcome to Demon School! Iruma-kun (2019)": 26889513042,
"P:\\anime\\Alya Sometimes Hides Her Feelings in Russian": 4232637768,
"P:\\anime\\Dead Mount Death Play (2023)": 9605188624,
"P:\\anime\\Demon Slayer - Kimetsu no Yaiba (2019)": 21376385353,
"P:\\anime\\Kaiju No. 8": 8770165001,
"P:\\anime\\DNAngel": 6235958953,
"P:\\anime\\Adam's Sweet Agony": 71,
"P:\\anime\\Leadale no Daichi nite": 1473622878,
"P:\\anime\\Isekai de Cheat Skill": 4311581943,
"P:\\anime\\Val x Love (2019)": 634008306,
"P:\\anime\\SHOSHIMIN - How to Become Ordinary": 21152251460,
"P:\\anime\\UchiMusume": 6085582077,
"P:\\anime\\Kino no Tabi": 3227343894,
"P:\\anime\\Shokei Shoujo no Virgin Road": 2660059361,
"P:\\anime\\Arknights": 5905302117,
"P:\\anime\\Don't Toy With Me Miss Nagatoro": 4485844086,
"P:\\anime\\Death March": 2905475121,
"P:\\anime\\Ascendance of a Bookworm": 5407349860,
"P:\\anime\\Loner Life in Another World": 25508136936,
"P:\\anime\\Blast of Tempest": 6315813655,
"P:\\anime\\The Eminence in Shadow": 12848813083,
"P:\\anime\\I'm Living with an Otaku NEET Kunoichi!! (2025)": 6529531138,
"P:\\anime\\Rent a girlfriend": 5319970861,
"P:\\anime\\B The Beginning": 9274520504,
"P:\\anime\\FLCL": 3233214173,
"P:\\anime\\Demon Lord 2099 (2024)": 4298604298,
"P:\\anime\\Fairy Gone": 5688255513,
"P:\\anime\\Fantasy \u00d7 Hunter (2021)": 3990876164,
"P:\\anime\\Outbreak Company": 6048060793,
"P:\\anime\\4 Cut Hero": 4259631497,
"P:\\anime\\Peach Boy Riverside": 1507820436,
"P:\\anime\\Lazarus (2025)": 5916959141,
"P:\\anime\\Ragna Crimson": 9249575950,
"P:\\anime\\Jujutsu Kaisen": 20957528147,
"P:\\anime\\Harem in the Labyrinth of Another World": 2871442057,
"P:\\anime\\Sekirei (2008)": 9623765425,
"P:\\anime\\Reign of the Seven Spellblades": 4104496247,
"P:\\anime\\By the Grace of the Gods": 5345602025,
"P:\\anime\\Captain Earth": 5810590243,
"P:\\anime\\Air Gear (2006)": 8918228002,
"P:\\anime\\My Instant Death Ability Is So Overpowered, No One in This Other World Stands a Chance Against Me!": 4454494342,
"P:\\anime\\Maburaho": 9553356823,
"P:\\anime\\One Pace": 5154953305,
"P:\\anime\\Love Tyrant": 2852328170,
"P:\\anime\\Sword Art Online (2012)": 42953654496,
"P:\\anime\\My Hero Academia": 91570180658,
"P:\\anime\\SAKAMOTO DAYS (2025)": 12135764735,
"P:\\anime\\No Guns Life (2019)": 12216429324,
"P:\\anime\\Undead Unluck": 8811674477,
"P:\\anime\\Guilty Crown": 6872382355,
"P:\\anime\\Radiant (2018)": 9278741705,
"P:\\anime\\Tokyo Revengers": 9193065093,
"P:\\anime\\How Not to Summon a Demon Lord": 6700089914,
"P:\\anime\\The Demon Sword Master of Excalibur Academy": 3453095058,
"P:\\anime\\Meikyuu Black Company": 2658074731,
"P:\\anime\\Toradora!": 6558115868,
"P:\\anime\\Maken-Ki! Battling Venus (2011)": 3500680574,
"P:\\anime\\Why Does Nobody Remember Me in This World! (2024)": 4152176021,
"P:\\anime\\YU-NO - A Girl Who Chants Love at the Bound of This World (2019)": 6254598586,
"P:\\anime\\Heroic Age": 7205431055,
"P:\\anime\\Solo Leveling": 16655700087,
"P:\\anime\\Cop Craft": 1913887249,
"P:\\anime\\Urusei Yatsura (2022)": 7969472192,
"P:\\anime\\Deepy Insanity The Lost Child": 8187291543,
"P:\\anime\\I'm the Evil Lord of an Intergalactic Empire! (2025)": 4135726123,
"P:\\anime\\King's Raid": 9278771931,
"P:\\anime\\Robotics;Notes": 5662173084,
"P:\\anime\\Kemono Jihen": 7461581399,
"P:\\anime\\Noragami": 7371770717,
"P:\\anime\\Food Wars": 39136542103,
"P:\\anime\\Kemono Michi Rise Up (2019)": 7529875102,
"P:\\anime\\Akudama Drive": 7276183519,
"P:\\anime\\Heavenly Delusion": 4930379667,
"P:\\anime\\High School of the Dead": 6051229299,
"P:\\anime\\Hero Without a Class - Who Even Needs Skills!! (2025)": 11550176573,
"P:\\anime\\Tears to Tiara": 6916843507,
"P:\\anime\\Mobile Suit Gundam - Iron-Blooded Orphans": 22688263314,
"P:\\anime\\Murenase! Seton Gakuen": 8774144557,
"P:\\anime\\The Brilliant Healer's New Life in the Shadows": 4196047668,
"P:\\anime\\The Kingdoms of Ruin": 4953422715,
"P:\\anime\\The Great Jahy Will Not Be Defeated! (2021)": 7300409701,
"P:\\anime\\D.Gray-man": 4272945740,
"P:\\anime\\Ya Boy Kongming! (2022)": 2371722402,
"P:\\anime\\Grendizer U": 8793468670,
"P:\\anime\\ReZERO -Starting Life in Another World": 34808814228,
"P:\\anime\\Taboo Tattoo": 3570322719,
"P:\\anime\\KamiKatsu - Working for God in a Godless World (2023)": 4033372211,
"P:\\anime\\Boogiepop Phantom": 3775812052,
"P:\\anime\\Hamatora": 6142541816,
"P:\\anime\\A Boring World Where the Concept of Dirty Jokes Doesn't Exist": 4384721870,
"P:\\anime\\Tribe Nine": 7128583972,
"P:\\anime\\Build Divide Code Black": 7356540221,
"P:\\anime\\I'm Quitting Heroing (2022)": 2641278879,
"P:\\anime\\THE NEW GATE (2024)": 2272505431,
"P:\\anime\\I Was Reincarnated as the 7th Prince so I Can Take My Time Perfecting My Magical Ability (2024)": 13202089851,
"P:\\anime\\Sword Art Online Alternative - Gun Gale Online": 3803411762,
"P:\\anime\\SANDA (2025)": 6601681293,
"P:\\anime\\Dolls' Frontline": 7376834741,
"P:\\anime\\Casshern Sins (2008)": 9460351881,
"P:\\anime\\Revenger": 2130039433,
"P:\\anime\\The Great Cleric": 3320612535,
"P:\\anime\\Spice and Wolf - MERCHANT MEETS THE WISE WOLF": 8173191986,
"P:\\anime\\Villainess Level 99 - I May Be the Hidden Boss but I'm Not the Demon Lord (2024)": 3561222609,
"P:\\anime\\Muv-Luv Alternative": 13357929759,
"P:\\anime\\And You Thought There Is Never a Girl Online": 4452676547,
"P:\\anime\\Grisaia no Kajitsu": 5151626219,
"P:\\anime\\Yandere Dark Elf - She Chased Me All the Way From Another World (2025)": 6378005867,
"P:\\anime\\Tatoeba Last Dungeon": 4836583236,
"P:\\anime\\ZENSHU (2025)": 15428885323,
"P:\\anime\\Shikizakura": 2018217197,
"P:\\anime\\Magical Warfare": 3590141409,
"P:\\anime\\The Most Notorious Talker Runs the World's Greatest Clan": 15191321279,
"P:\\anime\\Tales of Wedding Rings": 20310093214,
"P:\\anime\\One-Punch Man (2015)": 21480447907,
"P:\\anime\\Let This Grieving Soul Retire!": 24270650972,
"P:\\anime\\DEAD DEAD DEMONS DEDEDEDE DESTRUCTION": 6024955773,
"P:\\anime\\Brave Bang Bravern!": 5660773541,
"P:\\anime\\No Longer Allowed in Another World": 3507605900,
"P:\\anime\\Black Lagoon": 3530474230,
"P:\\anime\\Lapis ReLights": 4027545928,
"P:\\anime\\Rokka Braves of the Six Flowers": 5477020823,
"P:\\anime\\That Time I Got Reincarnated as a Slime (2018)": 21976147633,
"P:\\anime\\Your Lie in April (2014)": 3698563140,
"P:\\anime\\Amagi Brilliant Park": 2905611585,
"P:\\anime\\Ore dake Haireru Kakushi Dungeon": 4974018022,
"P:\\anime\\Overlord": 19235478562,
"P:\\anime\\Nozo X Kimi": 63,
"P:\\anime\\Life With an Ordinary Guy Who Reincarnated Into a Total Fantasy Knockout (2022)": 7944272179,
"P:\\anime\\Am I Actually the Strongest": 4272833723,
"P:\\anime\\Btooom!": 3101208509,
"P:\\anime\\Trigun Stampede": 13265836559,
"P:\\anime\\Overflow": 1602757167,
"P:\\anime\\Zom 100 - Bucket List of the Dead (2023)": 4941498609,
"P:\\anime\\Dusk Beyond the End of the World (2025)": 7418826849,
"P:\\anime\\Somali to Mori no Kamisama": 1461553114,
"P:\\anime\\Armed Girl's Machiavellism": 5134830058,
"P:\\anime\\Kaijin Kaihatsu-bu no Kuroitsu-san": 8782789658,
"P:\\anime\\Beheneko - The Elf-Girl's Cat Is Secretly an S-Ranked Monster! (2025)": 16419141963,
"P:\\anime\\Tatsuki Fujimoto 17-26 (2025)": 2165081620,
"P:\\anime\\I Couldn't Become a Hero, so I Reluctantly Decided To Get a Job (2013)": 3099289383,
"P:\\anime\\Etotama": 3578916471,
"P:\\anime\\Ranking of Kings": 14621466110,
"P:\\anime\\Divine Gate (2016)": 5316940542,
"P:\\anime\\Chained Soldier": 5342727117,
"P:\\anime\\Mashle": 17394820927,
"P:\\anime\\Frieren - Beyond Journey's End": 11535858054,
"P:\\anime\\Sakugan": 7367853913,
"P:\\anime\\Summer Time Render (2022)": 12201595066,
"P:\\anime\\Is It Wrong to Try to Pick Up Girls in a Dungeon": 14990233709,
"P:\\anime\\The Strongest Magician in the Demon Lord's Army was a Human": 2371668942,
"P:\\anime\\Hokkaido Gals Are Super Adorable!": 3798126702,
"P:\\anime\\Tsugumomo": 3588842344,
"P:\\anime\\Koi wa Sekai Seifuku no Ato de": 8842484921,
"P:\\anime\\Pseudo Harem": 11144999080,
"P:\\anime\\Rascal Does Not Dream of Bunny Girl Senpai (2018)": 22453345353,
"P:\\anime\\Dr.Stone": 8061461697,
"P:\\anime\\Kyuukyoku Shinka Shita Full Dive RPG ga Genjitsu yori mo Kusogee Dattara": 2946703318,
"P:\\anime\\The Daily Life of the Immortal King": 16185152763,
"P:\\anime\\Medaka Box": 7415352854,
"P:\\anime\\ID Invaded": 3399670994,
"P:\\anime\\Arifureta - From Commonplace to World's Strongest (2019)": 25705179606,
"P:\\anime\\So I'm a Spider, So What\uf025": 8812944012,
"P:\\anime\\Mobile Suit Gundam GQuuuuuuX (2025)": 6651663995,
"P:\\anime\\Kyokou Suiri": 8904306316,
"P:\\anime\\Punch Line": 3182400207,
"P:\\anime\\Spice and Wolf (2008)": 19280134842,
"P:\\anime\\Giant Beasts of Ars (2023)": 5083927761,
"P:\\anime\\Xam'd - Lost Memories (2008)": 5865586067,
"P:\\anime\\Fantasia Sango - Realm of Legends": 4577794096,
"P:\\anime\\The Devil Is a Part-Timer! (2013)": 12904787079,
"P:\\anime\\I'm the Villainess, So I'm Taming the Final Boss": 8846571865,
"P:\\anime\\The Faraway Paladin": 8109669375,
"P:\\anime\\2.5 Dimensional Seduction (2024)": 25882183452,
"P:\\anime\\Hortensia Saga": 7471222614,
"P:\\anime\\Yoasobi Gurashi! (2024)": 59,
"P:\\anime\\PLUTO (2023)": 10080474308,
"P:\\anime\\Code Geass - Roz\u00e9 of the Recapture": 12458298795,
"P:\\anime\\Sabikui Bisco": 8845023203,
"P:\\anime\\Dimension W": 6030623909,
"P:\\anime\\My One-Hit Kill Sister": 3952345545,
"P:\\anime\\Nura Rise of the Yokai Clan (2010)": 6880946329,
"P:\\anime\\Wandering Witch - The Journey of Elaina (2020)": 4009196941,
"P:\\anime\\Our Last Crusade or the Rise of a New World (2020)": 22376200276,
"P:\\anime\\Chillin' in My 30s after Getting Fired from the Demon King's Army (2023)": 8923284328,
"P:\\anime\\The Legend of the Legendary Heroes": 3321747819,
"P:\\anime\\Viral Hit (2024)": 4222663705,
"P:\\anime\\My Isekai Life (2023)": 5128126734,
"P:\\anime\\Danganronpa": 20640550319,
"P:\\anime\\The Apothecary Diaries (2023)": 13984546814,
"P:\\anime\\Do You Love Your Mom and Her Two-Hit Multi-Target Attacks! (2019)": 7458697426,
"P:\\anime\\Tondemo Skill de Isekai Hourou Meshi": 3743859331,
"P:\\anime\\The Unwanted Undead Adventurer": 3551145665,
"P:\\anime\\Horimiya": 4883874524,
"P:\\anime\\Majutsushi Orphen Hagure Tabi": 8043422997,
"P:\\anime\\Mahouka Koukou no Rettousei": 12595036509,
"P:\\anime\\Classroom of the elite": 4317927091,
"P:\\anime\\'Tis Time for Torture, Princess": 3456434199,
"P:\\anime\\To be Hero X (2025)": 9231210440,
"P:\\anime\\Enen no Shouboutai": 17624413412,
"P:\\anime\\New Saga (2025)": 3640546484,
"P:\\anime\\Triage X": 2888953825,
"P:\\anime\\The Fable": 6468034435,
"P:\\anime\\Good Night World": 6328301164,
"P:\\anime\\As a Reincarnated Aristocrat, I'll Use My Appraisal Skill To Rise in the World": 7971179978,
"P:\\anime\\Strike the Blood": 16209826871,
"P:\\anime\\Bakemonogatari": 3058292879,
"P:\\anime\\The Quintessential Quintuplets": 9307789725,
"P:\\anime\\Record of Ragnarok": 4724846924,
"P:\\anime\\The Case Study of Vanitas (2021)": 23448375312,
"P:\\anime\\Seven Mortal Sins": 6941414132,
"P:\\anime\\SPY x FAMILY (2022)": 32564008225,
"P:\\anime\\Fumetsu no Anata e": 30402249614,
"P:\\anime\\Edens Zero (2021)": 24008630035,
"P:\\anime\\Tokyo Majin": 8240109758,
"P:\\anime\\Saga of Tanya the Evil (2017)": 5356263633,
"P:\\anime\\Isekai Cheat Magician": 6956708991,
"P:\\anime\\Devils and Realist": 9782071975,
"P:\\anime\\Hero Return": 3795654831,
"P:\\anime\\Log Horizon": 30428201748,
"P:\\anime\\Akame ga Kill!": 9281445041,
"P:\\anime\\Dog & Scissors": 4070030221,
"P:\\anime\\Chainsaw Man (2022)": 3962550947,
"P:\\anime\\Toaru Kagaku no Accelerator": 4470774794,
"P:\\anime\\The Aristocrat\u2019s Otherworldly Adventure - Serving Gods Who Go Too Far (2023)": 14415952754,
"P:\\anime\\The Misfit of Demon King Academy": 11880070022,
"P:\\anime\\Chobits": 3750727774,
"P:\\anime\\Devil May Cry (2025)": 2598556744,
"P:\\anime\\Bloodivores": 1944037383,
"P:\\anime\\Plunderer": 11316610908,
"P:\\anime\\Steins;Gate": 4725618067,
"P:\\anime\\Shomin Sample": 8260184285,
"P:\\anime\\Oshi No Ko": 3823754198,
"P:\\anime\\Summoned to Another World for a Second Time (2023)": 8794846955,
"P:\\anime\\KonoSuba": 24362870774,
"P:\\anime\\Deadman Wonderland": 2631932266,
"P:\\anime\\Sword of the Demon Hunter - Kijin Gentosho (2025)": 25576883003,
"P:\\anime\\Call of the Night (2022)": 2104316829,
"P:\\anime\\Chaos Dragon (2015)": 8975083681,
"P:\\anime\\Sono Bisque Doll wa Koi o Suru": 4778669831,
"P:\\anime\\My Life as Inukai-san's Dog (2023)": 3968549174,
"P:\\anime\\Tensai Ouji no Akaji Kokka Saisei Jutsu": 7768516948,
"P:\\anime\\Mr. Villain's Day Off": 5828680585,
"P:\\anime\\My Girlfriend is Shobitch": 4575335545,
"P:\\anime\\Junketsu no Maria (Maria the Virgin Witch)": 3102318111,
"P:\\anime\\I Left my A-Rank Party to Help My Former Students Reach the Dungeon Depths!": 8709504239,
"P:\\anime\\Sunday Without God": 4038886818,
"P:\\anime\\Tate no Yuusha no Nariagari": 24178141736,
"P:\\anime\\You are Ms. Servant (2024)": 4971082404,
"P:\\anime\\Orient": 7905080784,
"P:\\anime\\Joran - The Princess of Snow and Blood (2021)": 4285203115,
"P:\\anime\\BOFURI I Don't Want to Get Hurt, so I'll Max Out My Defense. (2020)": 3933320248,
"P:\\anime\\Girls Bravo": 5883170984,
"P:\\anime\\Tantei wa Mou, Shindeiru": 1474340872,
"P:\\anime\\Black Summoner": 4567460661,
"P:\\anime\\Wistoria - Wand and Sword (2024)": 6857088069,
"P:\\anime\\Assassins Pride": 4073372515,
"P:\\anime\\GATE": 6973714236,
"P:\\anime\\Hunter x Hunter (2011)": 28776368971,
"P:\\anime\\WITCH WATCH (2025)": 36023680385,
"P:\\anime\\VanDread (2000)": 7637961912,
"P:\\anime\\Gibiate": 8871671340,
"P:\\anime\\Tojima Tanzaburo Wants to Be a Kamen Rider": 17458489042,
"P:\\anime\\Chiikawa": 201629499,
"P:\\anime\\Skeleton Knight in Another World": 4720673665,
"P:\\anime\\Welcome to the N.H.K. (2006)": 2905064932,
"P:\\anime\\Seirei Gensouki": 12072776481,
"P:\\anime\\Even Given the Worthless Appraiser Class, I'm Actually the Strongest (2025)": 3877147709,
"P:\\anime\\Seraph of the End": 8836558304,
"P:\\anime\\86 - Eighty Six (2021)": 11346777283,
"P:\\anime\\I May Be a Guild Receptionist, But I'll Solo Any Boss to Clock Out on Time (2025)": 3401364461,
"P:\\anime\\That Time I Got Reincarnated as a Slime (2018": 39885493376,
"P:\\anime\\Magi Adventure of Sinbad": 4379068780,
"P:\\anime\\Gosick": 7565536865,
"P:\\anime\\Lord Marksman and Vanadis": 3650176287,
"P:\\anime\\Aesthetica of a Rogue Hero": 3926285815,
"P:\\anime\\The Wrong Way To Use Healing Magic (2024)": 4887942602,
"P:\\anime\\Undead Girl Murder Farce": 1425415656,
"P:\\anime\\World's End Harem (2021)": 3758753966,
"P:\\anime\\The Healer who Was Banished From His Party, Is, In Fact, The Strongest": 3180656302,
"P:\\anime\\Great Pretender": 6791244533,
"P:\\anime\\Bye Bye, Earth (2024)": 6901980665,
"P:\\anime\\To LOVE-Ru (2008)": 24039134215,
"P:\\anime\\My Senpai is Annoying": 2636570562,
"P:\\anime\\Machikado Mazoku": 2372896297,
"P:\\anime\\Tsukimichi - Moonlit Fantasy (2021)": 6355047942,
"P:\\anime\\Beyond the Boundary (2013)": 10548084129,
"P:\\anime\\The God of High School": 5246905321,
"P:\\anime\\Suicide Squad Isekai (2024)": 9987227506,
"P:\\anime\\\u00dcbel Blatt (2025)": 4818553182,
"P:\\anime\\Fractale": 2349352593,
"P:\\anime\\Rosario + Vampire (2008)": 10733216754,
"P:\\anime\\THE RED RANGER Becomes an Adventurer in Another World (2025)": 4815193468,
"P:\\anime\\Wise Man's Grandchild (2019)": 5251808321,
"P:\\anime\\Liar Liar (2023)": 3742043301,
"P:\\anime\\Listeners": 7623419250,
"P:\\anime\\Tokyo Ghoul": 3007724887,
"P:\\anime\\Ninja Kamui": 5301196868,
"P:\\anime\\Apocalypse Hotel (2025)": 7672503907,
"P:\\anime\\Megami-ryou no Ryoubo-kun": 1113458183,
"P:\\anime\\Demon King Daimao": 3779405140,
"P:\\anime\\Shikkakumon no Saikyou Kenja": 12316965814,
"P:\\anime\\The Dawn of the Witch (2022)": 5090411078,
"P:\\anime\\An Archdemon's Dilemma - How To Love Your Elf Bride": 4148806570,
"P:\\anime\\Tower of God": 4300739979,
"P:\\anime\\Prison School": 5023091161,
"P:\\anime\\Domestic Girlfriend": 4526369861,
"P:\\anime\\I Parry Everything": 4757235421,
"P:\\anime\\Classroom for Heroes": 4024333345,
"P:\\anime\\My Hero Academia - Vigilantes (2025)": 6139355726,
"P:\\anime\\The Iceblade Sorcerer Shall Rule the World": 4957596749,
"P:\\anime\\A Terrified Teacher at Ghoul School!": 7877236936,
"P:\\anime\\Deca-Dence": 5223908805,
"P:\\anime\\Otomege Sekai wa Mob ni Kibishii Sekai desu": 4212433019,
"P:\\anime\\Hai to Gensou no Grimgar": 2769261873,
"P:\\anime\\Black Clover": 9257210643,
"P:\\anime\\The World's Finest Assassin Gets Reincarnated in Another World as an Aristocrat (2021)": 13724318948,
"P:\\anime\\Mob Psycho 100": 15587250285,
"P:\\anime\\Mecha-Ude - Mechanical Arms": 7833270141,
"P:\\anime\\Moonrise (2025)": 7415752085,
"P:\\anime\\Blood Lad (2013)": 3963930670,
"P:\\anime\\From Old Country Bumpkin to Master Swordsman (2025)": 5043093837,
"P:\\anime\\Cowboy Bebop": 10008334150,
"P:\\anime\\Toilet-Bound Hanako-kun (2020)": 7614925159,
"P:\\anime\\The Familiar of Zero": 11204697509,
"P:\\anime\\Servamp (2016)": 3825335418,
"P:\\anime\\Mushoku Tensei - Jobless Reincarnation (2021)": 28463561868,
"P:\\anime\\Masou Gakuen HxH": 5217076051,
"P:\\anime\\Knight's & Magic": 5462539205,
"P:\\anime\\DAN DA DAN (2024)": 10293363650,
"P:\\anime\\Terror in Resonance (2014)": 2651080911,
"P:\\anime\\Masamune-kun no Revenge": 2189592644,
"P:\\anime\\Darwin's Game": 2896376206,
"P:\\anime\\Green Green": 4573681221,
"P:\\anime\\Heion Sedai no Idaten-tachi": 4801708412,
"P:\\anime\\The Reincarnation of the Strongest Exorcist in Another World": 4530803804,
"P:\\anime\\My Daughter Left the Nest and Returned an S-Rank Adventurer (2023)": 8845481436,
"P:\\anime\\Hamefura": 8824881383,
"P:\\anime\\Recovery of an MMO Junkie": 2534587997,
"P:\\anime\\Astra Lost in Space (2019)": 5522863567,
"P:\\anime\\Mobile Suit Gundam The Witch from Mercury": 4233451823,
"P:\\anime\\Unnamed Memory": 6391216954,
"P:\\anime\\My Home Hero": 10316487117,
"P:\\anime\\Noblesse": 3106598797,
"P:\\anime\\Quality Assurance in Another World": 6689588963,
"P:\\anime\\The Saint's Magic Power is Omnipotent": 2353633162,
"P:\\anime\\Gods' Games We Play": 9805921920,
"P:\\anime\\High School D\u00d7D (2012)": 138488392829,
"P:\\anime\\Buddy Daddies": 3249476770,
"P:\\anime\\The Testament of Sister New Devil": 9051073176,
"P:\\anime\\Why the Hell are You Here, Teacher!! (2019)": 1437748496,
"P:\\anime\\Grenadier": 4272486895,
"P:\\anime\\.deletedByTMM": 2941377788,
"P:\\anime\\Dragonar Academy": 5046527432,
"P:\\anime\\Genjitsu Shugi Yuusha no Oukoku Saikenki": 10459881239,
"P:\\anime\\Darling in the FranXX": 8035916582,
"P:\\anime\\Platinum End": 7525590740,
"P:\\anime\\Go! Go! Loser Ranger! (2024)": 8801862587,
"P:\\anime\\Mission - Yozakura Family": 9503269395,
"P:\\anime\\Attack on Titan": 49531319741,
"P:\\anime\\The Kings Avatar": 8090986074,
"P:\\anime\\Gokukoku no Brynhildr": 1836411221,
"P:\\anime\\Pok\u00e9mon (1997)": 64280309172,
"P:\\anime\\Trigun": 7302317846,
"P:\\anime\\Failure Frame - I Became the Strongest and Annihilated Everything with Low-Level Spells (2024)": 14994809254,
"P:\\anime\\The Daily Life of a Middle-Aged Online Shopper in Another World (2025)": 20550758199,
"P:\\anime\\Vermeil in Gold (2022)": 3029688342,
"P:\\anime\\A Returner's Magic Should Be Special": 4013798232,
"P:\\anime\\The Legendary Hero Is Dead! (2023)": 3859024813,
"P:\\anime\\Golden Boy": 1780893800,
"P:\\anime\\Tenchi Muyo! War on Geminar": 5177920884,
"P:\\anime\\Tokyo Ravens": 3797091570,
"P:\\anime\\Ore wo Suki nano wa Omae dake ka yo": 4930678131,
"P:\\anime\\Beast Tamer": 4948757269
}

2877
.cache/.cache_movies.json Normal file

File diff suppressed because it is too large Load Diff

322
.cache/.cache_tv.json Normal file
View File

@ -0,0 +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\\High Potential": 24339309461,
"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 Pretender": 18425629462,
"P:\\tv\\The Queen's Gambit": 4100494817,
"P:\\tv\\The Rain (2018)": 2941174698,
"P:\\tv\\The Santa Clauses (2022)": 6400385164,
"P:\\tv\\The Second Best Hospital in the Galaxy (2024)": 3636394169,
"P:\\tv\\The Split": 7970767632,
"P:\\tv\\The Studio (2025)": 11530554023,
"P:\\tv\\The Take": 6020370013,
"P:\\tv\\The Terminal List - Dark Wolf (2025)": 9939046560,
"P:\\tv\\The Traitors (US) (2023)": 48149750078,
"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\\Twisted Metal (2023)": 12547412897,
"P:\\tv\\Um, Actually": 12360993522,
"P:\\tv\\Unstable": 5444623642,
"P:\\tv\\Utopia (AU)": 8691287022,
"P:\\tv\\Very Important People": 12237876110,
"P:\\tv\\Vice Principals (2016)": 18406955713,
"P:\\tv\\Vikings (2013)": 194095449878,
"P:\\tv\\Villainous (2017)": 1961793524,
"P:\\tv\\Walker": 5492500161,
"P:\\tv\\Wandavision": 10099450034,
"P:\\tv\\Welcome to Chippendales (2022)": 10423545837,
"P:\\tv\\Welcome to Wrexham": 66664948104,
"P:\\tv\\What If": 21312022582,
"P:\\tv\\Wildemount Wildlings (2025)": 3348907992,
"P:\\tv\\Winning Time - The Rise of the Lakers Dynasty (2022)": 37911197652,
"P:\\tv\\Wolf Pack": 6844099384,
"P:\\tv\\WondLa": 1399628000,
"P:\\tv\\Worst Cooks in America (2010)": 22063867049,
"P:\\tv\\Yellowstone (2018)": 89724605866,
"P:\\tv\\Young Sheldon": 21714069112,
"P:\\tv\\Your Honor (2020)": 25879839349
}

3201
.folder_cache.json Normal file

File diff suppressed because it is too large Load Diff

152
PROJECT_STRUCTURE.md Normal file
View File

@ -0,0 +1,152 @@
# AV1 Batch Video Transcoder - Project Structure
## Overview
This project is a modular batch video transcoding system using NVIDIA's av1_nvenc codec with intelligent audio stream processing and resolution handling.
## Architecture
### Entry Point
- **main.py** - CLI entry point with argument parsing
- Loads configuration from `config.xml`
- Initializes logger and CSV tracker
- Dispatches to `process_folder()` for batch processing
### Core Modules
#### `core/config_helper.py`
- XML configuration parser
- Returns dict with audio, encoding, and filter settings
- **Key Config:**
- `audio.stereo.high/medium`: Target bitrates for stereo audio (1080p/720p)
- `audio.multi_channel.low/medium`: Target bitrates for multichannel audio
- `encode.cq`: CQ values per content type (tv_720, tv_1080, movie_720, movie_1080, etc.)
- `encode.fallback`: Bitrate fallback settings (900k/1080p, 650k/720p, etc.)
- `extensions`: Video file types to process (mkv, mp4, etc.)
- `ignore_tags`: Files to skip (trailer, sample, etc.)
#### `core/logger_helper.py`
- Comprehensive logging to `logs/conversion.log`
- Captures source/target specs, audio decisions, bitrate info
- Separate handlers for console (INFO+) and file (DEBUG+)
#### `core/audio_handler.py`
- **`calculate_stream_bitrate(input_file, stream_index)`**: Extracts audio stream with ffmpeg `-c copy`, parses bitrate output, falls back to file size calculation
- **`get_audio_streams(input_file)`**: Detects all audio streams with robust bitrate calculation
- **`choose_audio_bitrate(channels, bitrate_kbps, audio_config, is_1080_class)`**: Returns (codec, target_bitrate) tuple
- Stereo 1080p+: >192k → encode; ≤192k → preserve ("copy")
- Stereo 720p: >160k → encode; ≤160k → preserve
- Multichannel: Encodes to low (384k) or medium (448k) based on current bitrate
#### `core/video_handler.py`
- **`get_source_resolution(input_file)`**: ffprobe detection of video dimensions
- **`determine_target_resolution(src_width, src_height, explicit_resolution)`**: Smart resolution logic
- If >1080p → scale to 1080p
- Else → preserve source resolution
- Can be overridden with explicit `--r 480/720/1080` argument
#### `core/encode_engine.py`
- **`run_ffmpeg(...)`**: Main FFmpeg encoding orchestration
- Builds command with av1_nvenc settings
- Per-stream audio codec/bitrate decisions
- Handles both CQ and Bitrate modes
- Logs detailed before/after specs
#### `core/process_manager.py`
- **`process_folder(...)`**: Main batch processing loop
- Classifies files as TV/Movie/Anime based on path
- Detects per-file source resolution
- Applies smart resolution defaults or explicit overrides
- Handles CQ → Bitrate fallback if size threshold exceeded
- Tracks results in `conversion_tracker.csv`
- Deletes originals after successful encoding
## Workflow
1. **User** runs: `python main.py "C:\Videos\TV\ShowName" --r 720 --m bitrate`
2. **main.py** parses args, loads config.xml
3. **process_manager** iterates video files in folder
4. For each file:
- **video_handler** detects source resolution
- **audio_handler** analyzes audio streams and calculates bitrates
- **encode_engine** builds FFmpeg command with smart audio/resolution settings
- FFmpeg encodes with per-stream audio decisions
- **tracker** logs results to CSV
5. **logger** captures all details to `logs/conversion.log`
## Configuration Examples
### Force 720p Bitrate Mode
```bash
python main.py "C:\Videos\TV\Show" --r 720 --m bitrate
```
### Force 1080p with CQ=28
```bash
python main.py "C:\Videos\Movies" --cq 28 --r 1080
```
### Smart Mode (preserve resolution, 4K→1080p)
```bash
python main.py "C:\Videos\Mixed"
```
### Force 480p (for low-res content)
```bash
python main.py "C:\Videos\OldTV" --r 480
```
## Audio Encoding Logic
### Decision Tree
```
Stereo audio?
├─ YES + 1080p: [>192kbps] ENCODE to 192k AAC, [≤192k] COPY
├─ YES + 720p: [>160kbps] ENCODE to 160k AAC, [≤160k] COPY
└─ NO (Multichannel 6ch+): ENCODE to 384k (low) or 448k (medium) AAC
```
### Rationale
- Preserves high-quality original audio when already well-compressed
- Re-encodes excessive bitrate audio to standard targets
- Handles stereo at different resolutions appropriately
- Normalizes multichannel to 6ch surround (5.1) or 2ch stereo
## Files Modified/Created
### New Modules (Session Work)
- ✅ `core/audio_handler.py` - NEW
- ✅ `core/video_handler.py` - NEW
- ✅ `core/encode_engine.py` - NEW
- ✅ `core/process_manager.py` - NEW
- ✅ `main.py` - REFACTORED (524 lines → 70 lines)
### Cleanup
- ✅ Deleted `core/ffmpeg_helper.py` (code moved to audio_handler)
- ✅ Deleted `core/process_helper.py` (empty)
- ✅ Deleted `core/tracker_helper.py` (empty)
### Enhanced
- ✅ `config.xml` - Added `<low>384000</low>` to multi_channel audio
- ✅ `transcode.bat` - Enhanced with job counting and status tracking
- ✅ `paths.txt` - Queue format with --r and --m flags
## Validation Checklist
- ✅ All modules pass Pylance syntax check
- ✅ All imports resolve correctly
- ✅ Config loads and provides expected keys
- ✅ No unused/deprecated files remain
- ✅ Project structure clean and maintainable
## Running Tests
Verify the complete system:
```bash
# Test imports
python -c "from core.audio_handler import *; from core.video_handler import *; print('OK')"
# Run on test folder
python main.py "C:\Test\Videos" --r 720 --m bitrate
# Check logs
cat logs/conversion.log
```

View File

@ -9,7 +9,7 @@
<processing_folder>processing</processing_folder>
<!-- File suffix added to encoded outputs -->
<suffix> -EHX</suffix>
<suffix> - [EHX]</suffix>
<!-- Allowed input extensions -->
<extensions>.mkv,.mp4</extensions>
@ -18,7 +18,7 @@
<ignore_tags>ehx</ignore_tags> <!-- ,megusta -->
<!-- Reduction ratio threshold: output must be <= this % of original or encoding fails -->
<reduction_ratio_threshold>0.7</reduction_ratio_threshold>
<reduction_ratio_threshold>0.75</reduction_ratio_threshold>
</general>
<!-- =============================
@ -27,6 +27,7 @@
<path_mappings>
<map from="P:\tv" to="/mnt/plex/tv" />
<map from="P:\anime" to="/mnt/plex/anime" />
<map from="P:\movies" to="/mnt/plex/movies" />
</path_mappings>
<!-- =============================

View File

@ -227,3 +227,13 @@ tv,Canada's Drag Race vs The World,Canada's Drag Race - Canada vs. The World - S
tv,Canada's Drag Race vs The World,Canada's Drag Race - Canada vs. The World - S01E05 - Spy Queens x264 AAC WEBDL-1080p SLAG -EHX.mkv,2576.93,641.31,24.9,Bitrate
tv,Canada's Drag Race vs The World,Canada's Drag Race - Canada vs. The World - S01E06 - Grand Finale x264 AAC WEBDL-1080p SLAG -EHX.mkv,2575.57,640.14,24.9,Bitrate
movie,N/A,2025-12-29 21-53-23 -EHX.mp4,343.3,47.9,14.0,CQ
movie,N/A,How the Grinch Stole Christmas (2000) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv,5626.25,3268.49,58.1,CQ
movie,N/A,Oppenheimer (2023) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv,9344.87,3040.3,32.5,1920x1080,1920x1080,1,32,CQ
movie,N/A,Pacific Rim (2013) x265 AAC 7.1 Bluray-1080p Tigole -EHX.mkv,5624.98,4029.53,71.6,1920x1080,1920x1080,2,32,CQ
movie,N/A,"Planes, Trains and Automobiles (1987) x265 AAC 5.1 Bluray-1080p afm72 -EHX.mkv",4489.88,2513.95,56.0,1920x1080,1920x1080,1,32,CQ
movie,N/A,Hackers (1995) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv,5601.42,2346.49,41.9,1920x804,1920x804,2,32,CQ
movie,N/A,Bullet Train (2022) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv,5857.37,2444.68,41.7,1920x804,1920x804,2,32,CQ
movie,N/A,The Truman Show (1998) x265 AAC 5.1 Bluray-1080p Silence -EHX.mkv,5152.45,2971.12,57.7,1918x1080,1918x1080,1,32,CQ
movie,N/A,John Wick - Chapter 4 (2023) x265 AAC 7.1 Bluray-1080p Tigole -EHX.mkv,8144.41,3288.02,40.4,1920x804,1920x804,1,32,CQ
movie,N/A,F1 (2025) x265 EAC3 7.1 Bluray-1080p SAMPA -EHX.mkv,7923.16,4044.9,51.1,1920x1080,1920x1080,1,32,CQ
movie,N/A,Starship Troopers (1997) x265 AAC 5.1 Bluray-1080p Tigole - [EHX].mkv,6522.97,4495.6,68.9,1920x1040,1920x1040,4,32,CQ

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

174
core/audio_handler.py Normal file
View File

@ -0,0 +1,174 @@
# core/audio_handler.py
"""Audio stream detection, bitrate calculation, and codec selection."""
import json
import os
import subprocess
import tempfile
from pathlib import Path
from core.logger_helper import setup_logger
logger = setup_logger(Path(__file__).parent.parent / "logs")
def calculate_stream_bitrate(input_file: Path, stream_index: int) -> int:
"""
Extract audio stream to temporary file using -c copy, capture bitrate from ffmpeg output.
Returns bitrate in kbps. Falls back to 0 (and uses metadata) if extraction fails.
Uses ffmpeg's reported bitrate which is more accurate than calculating from file size/duration.
"""
temp_fd, temp_audio_path = tempfile.mkstemp(suffix=".aac", dir=None)
os.close(temp_fd)
try:
# Step 1: Extract audio stream with -c copy (lossless extraction)
# ffmpeg outputs bitrate info to stderr
extract_cmd = [
"ffmpeg", "-y", "-i", str(input_file),
"-map", f"0:a:{stream_index}",
"-c", "copy",
temp_audio_path
]
logger.debug(f"Extracting audio stream {stream_index} to temporary file for bitrate calculation...")
result = subprocess.run(extract_cmd, capture_output=True, text=True, check=True)
# Step 2: Parse bitrate from ffmpeg's output (stderr)
# Look for line like: "bitrate= 457.7kbits/s"
bitrate_kbps = 0
for line in result.stderr.split("\n"):
if "bitrate=" in line:
# Extract bitrate value from line like "size= 352162KiB time=01:45:03.05 bitrate= 457.7kbits/s"
parts = line.split("bitrate=")
if len(parts) > 1:
bitrate_str = parts[1].strip().split("kbits/s")[0].strip()
try:
bitrate_kbps = int(float(bitrate_str))
logger.debug(f"Stream {stream_index}: Extracted bitrate from ffmpeg output: {bitrate_kbps} kbps")
break
except ValueError:
continue
# If we couldn't parse bitrate from output, fall back to calculation
if bitrate_kbps == 0:
logger.debug(f"Stream {stream_index}: Could not parse bitrate from ffmpeg output, calculating from file size...")
file_size_bytes = os.path.getsize(temp_audio_path)
# Get duration using ffprobe
duration_cmd = [
"ffprobe", "-v", "error",
"-show_entries", "format=duration",
"-of", "default=noprint_wrappers=1:nokey=1:noprint_wrappers=1",
temp_audio_path
]
duration_result = subprocess.run(duration_cmd, capture_output=True, text=True, check=True)
duration_seconds = float(duration_result.stdout.strip())
bitrate_kbps = int((file_size_bytes * 8) / duration_seconds / 1000)
logger.debug(f"Stream {stream_index}: Calculated bitrate from file: {bitrate_kbps} kbps")
return bitrate_kbps
except Exception as e:
logger.warning(f"Failed to calculate bitrate for stream {stream_index}: {e}. Will fall back to metadata.")
return 0
finally:
# Clean up temporary audio file
try:
if os.path.exists(temp_audio_path):
os.remove(temp_audio_path)
logger.debug(f"Deleted temporary audio file: {temp_audio_path}")
except Exception as e:
logger.warning(f"Could not delete temporary file {temp_audio_path}: {e}")
def get_audio_streams(input_file: Path):
"""
Detect audio streams and calculate robust bitrates by extracting each stream.
Returns list of (index, channels, calculated_bitrate_kbps, language, metadata_bitrate_kbps)
"""
cmd = [
"ffprobe","-v","error","-select_streams","a",
"-show_entries","stream=index,channels,bit_rate,tags=language",
"-of","json", str(input_file)
]
result = subprocess.run(cmd, capture_output=True, text=True)
data = json.loads(result.stdout)
streams = []
for stream_num, s in enumerate(data.get("streams", [])):
index = s["index"]
channels = s.get("channels", 2)
src_lang = s.get("tags", {}).get("language", "und")
bit_rate_meta = int(s.get("bit_rate", 0)) if s.get("bit_rate") else 0
# Calculate robust bitrate by extracting the audio stream
calculated_bitrate_kbps = calculate_stream_bitrate(input_file, stream_num)
# If calculation failed, fall back to metadata
if calculated_bitrate_kbps == 0:
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")
streams.append((index, channels, calculated_bitrate_kbps, src_lang, int(bit_rate_meta / 1000) if bit_rate_meta else 0))
return streams
def choose_audio_bitrate(channels: int, bitrate_kbps: int, audio_config: dict, is_1080_class: bool) -> tuple:
"""
Choose audio codec and bitrate based on channel count, detected bitrate, and resolution.
Returns tuple: (codec, target_bitrate_bps)
- codec: "aac", "libopus", or "copy" (to preserve original without re-encoding)
- target_bitrate_bps: target bitrate in bits/sec (0 if using "copy")
Rules:
Stereo + 1080p:
- Above 192k high (192k) with AAC
- At/below 192k preserve (copy)
Stereo + 720p:
- Above 160k medium (160k) with AAC
- At/below 160k preserve (copy)
Multi-channel:
- Below 384k low (384k) with AAC
- 384k to below medium low (384k) with AAC
- Medium and above medium with AAC
"""
# Normalize to 2ch or 6ch output
output_channels = 6 if channels >= 6 else 2
if output_channels == 2:
# Stereo logic
if is_1080_class:
# 1080p+ stereo
high_br = audio_config["stereo"]["high"]
if bitrate_kbps > (high_br / 1000): # Above 192k
return ("aac", high_br)
else:
# Preserve original
return ("copy", 0)
else:
# 720p stereo
medium_br = audio_config["stereo"]["medium"]
if bitrate_kbps > (medium_br / 1000): # Above 160k
return ("aac", medium_br)
else:
# Preserve original
return ("copy", 0)
else:
# Multi-channel (6ch+) logic
low_br = audio_config["multi_channel"]["low"]
medium_br = audio_config["multi_channel"]["medium"]
if bitrate_kbps < (medium_br / 1000):
# Below medium, use low
return ("aac", low_br)
else:
# Medium and above, use medium
return ("aac", medium_br)

117
core/encode_engine.py Normal file
View File

@ -0,0 +1,117 @@
# core/encode_engine.py
"""FFmpeg encoding engine with comprehensive logging."""
import subprocess
from pathlib import Path
from core.audio_handler import get_audio_streams, choose_audio_bitrate
from core.logger_helper import setup_logger
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,
src_width: int, src_height: int, filter_flags: str, audio_config: dict,
method: str, bitrate_config: dict):
"""
Run FFmpeg encode with comprehensive logging.
Returns tuple: (orig_size, out_size, reduction_ratio)
"""
streams = get_audio_streams(input_file)
# Log comprehensive encode settings
header = f"\n🧩 ENCODE SETTINGS"
logger.info(header)
print(" ")
logger.info(f" Video:")
logger.info(f" • Source Resolution: {src_width}x{src_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" • Scale Filter: {filter_flags}")
logger.info(f" • Encode Method: {method}")
if method == "CQ":
logger.info(f" • CQ Value: {cq}")
else:
res_key = "1080" if scale_height >= 1080 or scale_width >= 1920 else "720"
vb = bitrate_config.get(f"bitrate_{res_key}", "900k")
maxrate = bitrate_config.get(f"maxrate_{res_key}", "1250k")
logger.info(f" • Bitrate: {vb}, Max: {maxrate}")
logger.info(f" Audio Streams ({len(streams)} detected):")
print(" ")
for (index, channels, avg_bitrate, src_lang, meta_bitrate) in streams:
# Normalize to 2ch or 6ch output
is_1080_class = scale_height >= 1080 or scale_width >= 1920
output_channels = 6 if is_1080_class and channels >= 6 else 2
codec, br = choose_audio_bitrate(output_channels, avg_bitrate, audio_config, is_1080_class)
if codec == "copy":
action = "COPY (preserve)"
bitrate_display = f"{avg_bitrate}kbps"
else:
action = "ENCODE"
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}"
print(line)
logger.info(line)
cmd = ["ffmpeg","-y","-i",str(input_file),
"-vf",f"scale={scale_width}:{scale_height}:flags={filter_flags}:force_original_aspect_ratio=decrease",
"-map","0:v","-map","0:a","-map","0:s?",
"-c:v","av1_nvenc","-preset","p1","-pix_fmt","p010le"]
if method=="CQ":
cmd += ["-cq", str(cq)]
else:
# Use bitrate config (fallback mode)
res_key = "1080" if scale_height >= 1080 or scale_width >= 1920 else "720"
vb = bitrate_config.get(f"bitrate_{res_key}", "900k")
maxrate = bitrate_config.get(f"maxrate_{res_key}", "1250k")
bufsize = bitrate_config.get(f"bufsize_{res_key}", "1800k")
cmd += ["-b:v", vb, "-maxrate", maxrate, "-bufsize", bufsize]
for i, (index, channels, avg_bitrate, src_lang, meta_bitrate) in enumerate(streams):
# Normalize to 2ch or 6ch output
is_1080_class = scale_height >= 1080 or scale_width >= 1920
output_channels = 6 if is_1080_class and channels >= 6 else 2
codec, br = choose_audio_bitrate(output_channels, avg_bitrate, audio_config, is_1080_class)
if codec == "copy":
# Preserve original audio
cmd += [f"-c:a:{i}", "copy"]
else:
# Re-encode with target bitrate
cmd += [
f"-c:a:{i}", codec,
f"-b:a:{i}", str(br),
f"-ac:{i}", str(output_channels),
f"-channel_layout:a:{i}", "5.1" if output_channels == 6 else "stereo"
]
cmd += ["-c:s","copy",str(output_file)]
print(f"\n🎬 Running {method} encode: {output_file.name}")
logger.info(f"Running {method} encode: {output_file.name}")
subprocess.run(cmd, check=True)
orig_size = input_file.stat().st_size
out_size = output_file.stat().st_size
reduction_ratio = out_size / orig_size
# Log comprehensive results
logger.info(f"\n📊 ENCODE RESULTS:")
logger.info(f" Original Size: {orig_size/1e6:.2f} MB")
logger.info(f" Encoded Size: {out_size/1e6:.2f} MB")
logger.info(f" Reduction: {reduction_ratio:.1%} of original ({(1-reduction_ratio):.1%} saved)")
logger.info(f" Resolution: {src_width}x{src_height}{scale_width}x{scale_height}")
logger.info(f" Audio Streams: {len(streams)} streams processed")
msg = f"📦 Original: {orig_size/1e6:.2f} MB → Encoded: {out_size/1e6:.2f} MB ({reduction_ratio:.1%} of original)"
print(msg)
return orig_size, out_size, reduction_ratio

View File

@ -1,207 +0,0 @@
# core/ffmpeg_helper.py
import json
import os
import subprocess
import tempfile
from pathlib import Path
from typing import Tuple
from core.logger_helper import setup_logger
logger = setup_logger(Path(__file__).parent.parent / "logs")
# =============================
# ROBUST BITRATE CALCULATION
# =============================
def calculate_stream_bitrate(input_file: Path, stream_index: int) -> int:
"""
Extract audio stream to temporary file using -c copy, calculate bitrate from file size and duration.
Returns bitrate in kbps.
Formula: bitrate_kbps = (file_size_bytes * 8) / duration_seconds / 1000
"""
temp_fd, temp_audio_path = tempfile.mkstemp(suffix=".aac", dir=None)
os.close(temp_fd)
try:
# Step 1: Extract audio stream with -c copy (lossless extraction)
extract_cmd = [
"ffmpeg", "-y", "-i", str(input_file),
"-map", f"0:a:{stream_index}",
"-c", "copy",
temp_audio_path
]
logger.debug(f"Extracting audio stream {stream_index} to temporary file...")
subprocess.run(extract_cmd, capture_output=True, text=True, check=True)
# Step 2: Get duration using ffprobe
duration_cmd = [
"ffprobe", "-v", "error",
"-show_entries", "format=duration",
"-of", "default=noprint_wrappers=1:nokey=1:noprint_wrappers=1",
temp_audio_path
]
duration_result = subprocess.run(duration_cmd, capture_output=True, text=True, check=True)
duration_seconds = float(duration_result.stdout.strip())
# Step 3: Get file size and calculate bitrate
file_size_bytes = os.path.getsize(temp_audio_path)
bitrate_kbps = int((file_size_bytes * 8) / duration_seconds / 1000)
logger.debug(f"Stream {stream_index}: size={file_size_bytes} bytes, duration={duration_seconds:.2f}s, calculated_bitrate={bitrate_kbps} kbps")
return bitrate_kbps
except Exception as e:
logger.warning(f"Failed to calculate bitrate for stream {stream_index}: {e}. Falling back to metadata.")
return 0
finally:
# Clean up temporary audio file
try:
if os.path.exists(temp_audio_path):
os.remove(temp_audio_path)
logger.debug(f"Deleted temporary audio file: {temp_audio_path}")
except Exception as e:
logger.warning(f"Could not delete temporary file {temp_audio_path}: {e}")
def get_audio_streams(input_file: Path):
"""Return a list of (index, channels, bitrate_kbps, lang)
Uses robust bitrate calculation by extracting each stream and computing
bitrate from file size and duration instead of relying on metadata.
"""
cmd = [
"ffprobe", "-v", "error",
"-select_streams", "a",
"-show_entries", "stream=index,channels,bit_rate,tags=language",
"-of", "json", str(input_file)
]
result = subprocess.run(cmd, capture_output=True, text=True)
data = json.loads(result.stdout or "{}")
streams = []
for i, s in enumerate(data.get("streams", [])):
index = s["index"]
channels = s.get("channels", 2)
lang = s.get("tags", {}).get("language", "und")
# Calculate robust bitrate from extraction
calculated_bitrate = calculate_stream_bitrate(input_file, i)
# Fallback to metadata if calculation fails
if calculated_bitrate == 0:
bitrate = int(int(s.get("bit_rate", 128000)) / 1000)
logger.info(f"Stream {index}: Using metadata bitrate {bitrate} kbps (calculation failed)")
else:
bitrate = calculated_bitrate
logger.info(f"Stream {index}: Using calculated bitrate {bitrate} kbps")
streams.append((index, channels, bitrate, lang))
return streams
# =============================
# AUDIO DECISION LOGIC
# =============================
def choose_audio_settings(channels: int, bitrate_kbps: int, audio_config: dict) -> Tuple[str, int]:
"""
Return (codec, target_bitrate)
Rules:
- If 128 kbps or lower use Opus
- Otherwise use AAC
- Use audio_config to bucket bitrates.
"""
if channels == 2:
if bitrate_kbps <= 80:
target_br = audio_config["stereo"]["low"]
elif bitrate_kbps <= 112:
target_br = audio_config["stereo"]["medium"]
else:
target_br = audio_config["stereo"]["high"]
else:
if bitrate_kbps <= 176:
target_br = audio_config["multi_channel"]["low"]
else:
target_br = audio_config["multi_channel"]["high"]
# Opus threshold: <=128 kbps
threshold = audio_config.get("use_opus_below_kbps", 128)
codec = "libopus" if target_br <= threshold * 1000 else "aac"
return codec, target_br
# =============================
# FFMPEG COMMAND BUILDER
# =============================
def build_ffmpeg_command(input_file: Path, output_file: Path,
cq: int, width: int, height: int,
filter_flags: str, audio_config: dict):
"""Builds FFmpeg command with smart audio logic."""
streams = get_audio_streams(input_file)
logger.info(f"🎛 Detected {len(streams)} audio stream(s). Building command...")
cmd = [
"ffmpeg", "-y", "-i", str(input_file),
"-vf", f"scale={width}:{height}:flags={filter_flags}:force_original_aspect_ratio=decrease",
"-map", "0:v", "-map", "0:a", "-map", "0:s?",
"-c:v", "av1_nvenc", "-preset", "p1", "-cq", str(cq),
"-pix_fmt", "p010le"
]
for i, (index, channels, bitrate, lang) in enumerate(streams):
codec, br = choose_audio_settings(channels, bitrate, audio_config)
cmd += [
f"-c:a:{i}", codec,
f"-b:a:{i}", str(br),
f"-ac:{i}", str(channels),
f"-metadata:s:a:{i}", f"language={lang}"
]
cmd += ["-c:s", "copy", str(output_file)]
return cmd, streams
# =============================
# ENCODE RUNNER
# =============================
def run_encode(input_file: Path, output_file: Path, cq: int,
width: int, height: int, filter_flags: str,
audio_config: dict):
"""Handles encode, fallback logic, and returns size stats."""
cmd, streams = build_ffmpeg_command(input_file, output_file, cq, width, height, filter_flags, audio_config)
logger.info(f"🎬 Running FFmpeg CQ encode → {output_file.name}")
subprocess.run(cmd, check=True)
# Size check
orig_size = input_file.stat().st_size
out_size = output_file.stat().st_size
ratio = out_size / orig_size
logger.info(f"📦 Size: {orig_size/1e6:.2f}MB → {out_size/1e6:.2f}MB ({ratio:.1%})")
# Fallback logic
if ratio >= 0.5:
logger.warning(f"⚠️ Reduction too low ({ratio:.0%}), retrying with bitrate mode...")
output_file.unlink(missing_ok=True)
vb, maxrate, bufsize = (
("1500k", "1750k", "2250k") if height >= 1080
else ("900k", "1250k", "1600k")
)
cmd = [
"ffmpeg", "-y", "-i", str(input_file),
"-vf", f"scale={width}:{height}:flags={filter_flags}:force_original_aspect_ratio=decrease",
"-map", "0:v", "-map", "0:a", "-map", "0:s?",
"-c:v", "av1_nvenc", "-preset", "p1",
"-b:v", vb, "-maxrate", maxrate, "-bufsize", bufsize,
"-pix_fmt", "p010le"
]
for i, (index, channels, bitrate, lang) in enumerate(streams):
codec, br = choose_audio_settings(channels, bitrate, audio_config)
cmd += [
f"-c:a:{i}", codec,
f"-b:a:{i}", str(br),
f"-ac:{i}", str(channels),
f"-metadata:s:a:{i}", f"language={lang}"
]
cmd += ["-c:s", "copy", str(output_file)]
subprocess.run(cmd, check=True)
return orig_size, out_size

226
core/hardware_helper.py Normal file
View File

@ -0,0 +1,226 @@
#!/usr/bin/env python3
"""
Hardware Detection and Optimization Module
Detects available hardware (GPU, CPU) and recommends optimal encoding settings.
"""
import subprocess
import platform
import json
from pathlib import Path
from .logger_helper import setup_logger
logger = setup_logger(Path(__file__).parent.parent / "logs")
class HardwareInfo:
"""Detects and stores hardware capabilities."""
def __init__(self):
self.platform_name = platform.system() # Windows, Linux, Darwin
self.cpu_cores = self._get_cpu_cores()
self.gpu_type = self._detect_gpu()
self.available_encoders = self._check_ffmpeg_encoders()
self.recommended_encoder = self._recommend_encoder()
self.recommended_settings = self._get_recommended_settings()
def _get_cpu_cores(self):
"""Get number of CPU cores."""
import multiprocessing
return multiprocessing.cpu_count()
def _detect_gpu(self):
"""Detect GPU type (NVIDIA, AMD, Intel, None)."""
if self.platform_name == "Windows":
return self._detect_gpu_windows()
elif self.platform_name == "Linux":
return self._detect_gpu_linux()
else:
return None
def _detect_gpu_windows(self):
"""Detect GPU on Windows using DXDIAG or WMI."""
try:
# Try using wmic (Windows only)
result = subprocess.run(
["wmic", "path", "win32_videocontroller", "get", "name"],
capture_output=True,
text=True,
timeout=5
)
gpu_info = result.stdout.lower()
if "nvidia" in gpu_info or "geforce" in gpu_info or "quadro" in gpu_info:
return "nvidia"
elif "amd" in gpu_info or "radeon" in gpu_info:
return "amd"
elif "intel" in gpu_info:
return "intel"
except Exception as e:
logger.warning(f"Could not detect GPU via WMI: {e}")
return None
def _detect_gpu_linux(self):
"""Detect GPU on Linux using lspci."""
try:
result = subprocess.run(
["lspci"],
capture_output=True,
text=True,
timeout=5
)
gpu_info = result.stdout.lower()
if "nvidia" in gpu_info:
return "nvidia"
elif "amd" in gpu_info:
return "amd"
elif "intel" in gpu_info:
return "intel"
except Exception as e:
logger.warning(f"Could not detect GPU via lspci: {e}")
return None
def _check_ffmpeg_encoders(self):
"""Check available encoders in FFmpeg."""
encoders = {
"h264_nvenc": False,
"hevc_nvenc": False,
"h264_amf": False,
"hevc_amf": False,
"h264_qsv": False,
"hevc_qsv": False,
"libx264": False,
"libx265": False,
}
try:
result = subprocess.run(
["ffmpeg", "-encoders"],
capture_output=True,
text=True,
timeout=5
)
output = result.stdout.lower()
for encoder in encoders:
if encoder in output:
encoders[encoder] = True
except Exception as e:
logger.warning(f"Could not check FFmpeg encoders: {e}")
return encoders
def _recommend_encoder(self):
"""Recommend best encoder based on hardware."""
# Prefer NVIDIA NVENC > AMD AMF > Intel QSV > CPU
if self.gpu_type == "nvidia":
if self.available_encoders.get("hevc_nvenc"):
return "hevc_nvenc" # H.265 on NVIDIA is efficient
elif self.available_encoders.get("h264_nvenc"):
return "h264_nvenc"
if self.gpu_type == "amd":
if self.available_encoders.get("hevc_amf"):
return "hevc_amf"
elif self.available_encoders.get("h264_amf"):
return "h264_amf"
if self.gpu_type == "intel":
if self.available_encoders.get("hevc_qsv"):
return "hevc_qsv"
elif self.available_encoders.get("h264_qsv"):
return "h264_qsv"
# Fallback to CPU encoders
if self.available_encoders.get("libx265"):
return "libx265"
elif self.available_encoders.get("libx264"):
return "libx264"
return "libx264" # Default fallback
def _get_recommended_settings(self):
"""Get recommended encoding settings based on hardware."""
settings = {
"encoder": self.recommended_encoder,
"preset": self._get_preset(),
"threads": self._get_thread_count(),
"gpu_capable": self.gpu_type is not None,
}
return settings
def _get_preset(self):
"""Get recommended preset based on encoder."""
encoder = self.recommended_encoder
# GPU encoders don't use "preset" in the same way
if "nvenc" in encoder or "amf" in encoder or "qsv" in encoder:
# GPU presets (0-fast, 1-medium, 2-slow)
return 1 # medium (balanced)
else:
# CPU presets (ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow)
if self.cpu_cores <= 4:
return "faster"
elif self.cpu_cores <= 8:
return "fast"
else:
return "medium"
def _get_thread_count(self):
"""Get recommended thread count for encoding."""
# For CPU encoding, use most cores but leave some for system
if "nvenc" in self.recommended_encoder or "amf" in self.recommended_encoder or "qsv" in self.recommended_encoder:
return 0 # GPU handles it
else:
# Leave 1-2 cores for system
return max(self.cpu_cores - 2, 1)
def to_dict(self):
"""Return all hardware info as dictionary."""
return {
"platform": self.platform_name,
"cpu_cores": self.cpu_cores,
"gpu_type": self.gpu_type,
"recommended_encoder": self.recommended_encoder,
"available_encoders": {k: v for k, v in self.available_encoders.items() if v},
"recommended_settings": self.recommended_settings,
}
def print_summary(self):
"""Print hardware detection summary."""
print("\n" + "="*60)
print("HARDWARE DETECTION SUMMARY")
print("="*60)
print(f"Platform: {self.platform_name}")
print(f"CPU Cores: {self.cpu_cores}")
print(f"GPU Type: {self.gpu_type or 'None (CPU only)'}")
print(f"\nAvailable Encoders:")
for encoder, available in self.available_encoders.items():
status = "" if available else ""
print(f" {status} {encoder}")
print(f"\nRecommended:")
print(f" Encoder: {self.recommended_encoder}")
print(f" Preset: {self.recommended_settings.get('preset')}")
print(f" Threads: {self.recommended_settings.get('threads')}")
print("="*60 + "\n")
def detect_hardware():
"""Quick detection - returns HardwareInfo object."""
return HardwareInfo()
if __name__ == "__main__":
# Test the hardware detection
hw = detect_hardware()
hw.print_summary()
# Also save as JSON for reference
hw_json = hw.to_dict()
print("Full Hardware Info (JSON):")
print(json.dumps(hw_json, indent=2))

View File

@ -1,35 +1,96 @@
# core/logger_helper.py
import logging
import json
from logging.handlers import RotatingFileHandler
from pathlib import Path
from datetime import datetime
class JsonFormatter(logging.Formatter):
"""
Custom JSON log formatter for structured logging.
"""
def format(self, record: logging.LogRecord) -> str:
log_object = {
"timestamp": datetime.utcfromtimestamp(record.created).strftime("%Y-%m-%dT%H:%M:%SZ"),
"level": record.levelname,
"message": record.getMessage(),
"module": record.module,
"funcName": record.funcName,
"line": record.lineno,
}
# Include any extra fields added via logger.info("msg", extra={...})
if hasattr(record, "extra") and isinstance(record.extra, dict):
log_object.update(record.extra)
# Include exception info if present
if record.exc_info:
log_object["exception"] = self.formatException(record.exc_info)
return json.dumps(log_object, ensure_ascii=False)
def setup_logger(log_folder: Path, log_file_name: str = "conversion.log", level=logging.INFO) -> logging.Logger:
"""
Sets up a logger that prints to console and writes to a rotating log file.
Sets up a logger that prints to console and writes to a rotating JSON log file.
"""
log_folder.mkdir(parents=True, exist_ok=True)
log_file = log_folder / log_file_name
logger = logging.getLogger("conversion_logger")
logger.setLevel(level)
logger.propagate = False # Prevent duplicate logging if root logger exists
logger.propagate = False # Prevent double logging
# Formatter with timestamp
formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
# Formatters
text_formatter = logging.Formatter(
"%(asctime)s [%(levelname)s] %(message)s (%(module)s:%(lineno)d)",
datefmt="%Y-%m-%d %H:%M:%S"
)
json_formatter = JsonFormatter()
# Console handler
# Console handler (human-readable)
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
console_handler.setFormatter(text_formatter)
console_handler.setLevel(level)
# File handler with rotation (max 5 MB per file, keep 3 backups)
file_handler = RotatingFileHandler(log_file, maxBytes=5*1024*1024, backupCount=3, encoding="utf-8")
file_handler.setFormatter(formatter)
# File handler (JSON logs)
file_handler = RotatingFileHandler(log_file, maxBytes=5 * 1024 * 1024, backupCount=3, encoding="utf-8")
file_handler.setFormatter(json_formatter)
file_handler.setLevel(level)
# Add handlers
# Add handlers only once
if not logger.handlers:
logger.addHandler(console_handler)
logger.addHandler(file_handler)
return logger
def setup_failure_logger(log_folder: Path) -> logging.Logger:
"""
Setup a dedicated logger for encoding failures.
Returns a logger that writes to logs/failure.log
"""
log_folder.mkdir(parents=True, exist_ok=True)
log_file = log_folder / "failure.log"
logger = logging.getLogger("failure_logger")
logger.setLevel(logging.WARNING)
# Prevent duplicate handlers
if logger.handlers:
return logger
# Simple text formatter for failure log
formatter = logging.Formatter(
"%(asctime)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
# File handler only
file_handler = RotatingFileHandler(log_file, maxBytes=5 * 1024 * 1024, backupCount=3, encoding="utf-8")
file_handler.setFormatter(formatter)
file_handler.setLevel(logging.WARNING)
logger.addHandler(file_handler)
logger.propagate = False
return logger

View File

398
core/process_manager.py Normal file
View File

@ -0,0 +1,398 @@
# core/process_manager.py
"""Main processing logic for batch transcoding."""
import csv
import shutil
import subprocess
from pathlib import Path
from core.audio_handler import get_audio_streams
from core.encode_engine import run_ffmpeg
from core.logger_helper import setup_logger, setup_failure_logger
from core.video_handler import get_source_resolution, determine_target_resolution
logger = setup_logger(Path(__file__).parent.parent / "logs")
failure_logger = setup_failure_logger(Path(__file__).parent.parent / "logs")
def _cleanup_temp_files(temp_input: Path, temp_output: Path):
"""Helper function to clean up temporary input and output files."""
try:
if temp_input.exists():
temp_input.unlink()
logger.debug(f"Cleaned up temp input: {temp_input.name}")
except Exception as e:
logger.warning(f"Could not delete temp input {temp_input.name}: {e}")
try:
if temp_output.exists():
temp_output.unlink()
logger.debug(f"Cleaned up temp output: {temp_output.name}")
except Exception as 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):
"""
Process all video files in folder with appropriate encoding settings.
Args:
folder: Input folder path
cq: CQ override value
transcode_mode: "cq" or "bitrate"
resolution: Explicit resolution override ("480", "720", "1080", or None for smart)
config: Configuration dictionary
tracker_file: Path to CSV tracker file
"""
if not folder.exists():
print(f"❌ Folder not found: {folder}")
logger.error(f"Folder not found: {folder}")
return
audio_config = config["audio"]
bitrate_config = config["encode"]["fallback"]
filters_config = config["encode"]["filters"]
suffix = config["suffix"]
extensions = config["extensions"]
ignore_tags = config["ignore_tags"]
reduction_ratio_threshold = config["reduction_ratio_threshold"]
# Resolution logic: explicit arg takes precedence, else use smart defaults
explicit_resolution = resolution # Will be None if not specified
filter_flags = filters_config.get("default","lanczos")
folder_lower = str(folder).lower()
is_tv = "\\tv\\" in folder_lower or "/tv/" in folder_lower
if is_tv:
filter_flags = filters_config.get("tv","bicubic")
processing_folder = Path(config["processing_folder"])
processing_folder.mkdir(parents=True, exist_ok=True)
# Determine if we're in smart mode (no explicit mode specified)
is_smart_mode = transcode_mode not in ["cq", "bitrate"] # Default/smart mode
is_forced_cq = transcode_mode == "cq"
is_forced_bitrate = transcode_mode == "bitrate"
# Track files for potential retry in smart mode
failed_cq_files = [] # List of (file_path, metadata) for CQ failures in smart mode
consecutive_failures = 0
max_consecutive = 3
# Phase 1: Process files with initial mode strategy
print(f"\n{'='*60}")
if is_smart_mode:
print("📋 MODE: Smart (Try CQ first, retry with Bitrate if needed)")
elif is_forced_cq:
print("📋 MODE: Forced CQ (skip failures, log them)")
else:
print("📋 MODE: Forced Bitrate (skip failures, log them)")
print(f"{'='*60}\n")
for file in folder.rglob("*"):
if file.suffix.lower() not in extensions:
continue
if any(tag.lower() in file.name.lower() for tag in ignore_tags):
print(f"⏭️ Skipping: {file.name}")
logger.info(f"Skipping: {file.name}")
continue
print("="*60)
logger.info(f"Processing: {file.name}")
print(f"📁 Processing: {file.name}")
temp_input = processing_folder / file.name
shutil.copy2(file, temp_input)
logger.info(f"Copied {file.name}{temp_input.name}")
try:
# Detect source resolution and determine target resolution
src_width, src_height = get_source_resolution(temp_input)
res_width, res_height, target_resolution = determine_target_resolution(
src_width, src_height, explicit_resolution
)
# Log resolution decision
if explicit_resolution:
logger.info(f"Using explicitly specified resolution: {res_width}x{res_height}")
else:
if src_height > 1080:
print(f"⚠️ Source {src_width}x{src_height} is above 1080p. Scaling down to 1080p.")
logger.info(f"Source {src_width}x{src_height} detected. Scaling to 1080p.")
elif src_height <= 720:
print(f" Source {src_width}x{src_height} is 720p or lower. Preserving resolution.")
logger.info(f"Source {src_width}x{src_height} (<=720p). Preserving source resolution.")
else:
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.")
# Set CQ based on content type and target resolution
content_cq = config["encode"]["cq"].get(f"tv_{target_resolution}" if is_tv else f"movie_{target_resolution}", 32)
file_cq = cq if cq is not None else content_cq
temp_output = processing_folder / f"{file.stem}{suffix}{file.suffix}"
# Determine which method to try first
if is_forced_bitrate:
method = "Bitrate"
elif is_forced_cq:
method = "CQ"
else: # Smart mode
method = "CQ" # Always try CQ first in smart mode
# Attempt encoding
try:
orig_size, out_size, reduction_ratio = run_ffmpeg(
temp_input, temp_output, file_cq, res_width, res_height, src_width, src_height,
filter_flags, audio_config, method, bitrate_config
)
# Check if encode met size target
encode_succeeded = True
if method == "CQ" and reduction_ratio >= reduction_ratio_threshold:
encode_succeeded = False
elif method == "Bitrate" and reduction_ratio >= reduction_ratio_threshold:
encode_succeeded = False
if not encode_succeeded:
# Size threshold not met
if is_smart_mode and method == "CQ":
# In smart mode CQ failure, mark for bitrate retry
print(f"⚠️ CQ failed size target ({reduction_ratio:.1%}). Will retry with Bitrate.")
failure_logger.warning(f"{file.name} | CQ failed size target ({reduction_ratio:.1%})")
failed_cq_files.append({
'file': file,
'temp_input': temp_input,
'temp_output': temp_output,
'src_width': src_width,
'src_height': src_height,
'res_width': res_width,
'res_height': res_height,
'target_resolution': target_resolution,
'file_cq': file_cq,
'is_tv': is_tv
})
consecutive_failures += 1
if consecutive_failures >= max_consecutive:
print(f"\n⚠️ {max_consecutive} consecutive CQ failures. Moving to Phase 2: Bitrate retry.")
logger.warning(f"{max_consecutive} consecutive CQ failures. Moving to Phase 2.")
break # Move to Phase 2
continue
elif is_forced_cq or is_forced_bitrate:
# In forced mode, skip the file
error_msg = f"Size threshold not met ({reduction_ratio:.1%})"
print(f"{method} failed: {error_msg}")
failure_logger.warning(f"{file.name} | {method} failed: {error_msg}")
consecutive_failures += 1
if consecutive_failures >= max_consecutive:
print(f"\n{max_consecutive} consecutive failures in forced {method} mode. Stopping.")
logger.error(f"{max_consecutive} consecutive failures. Stopping process.")
_cleanup_temp_files(temp_input, temp_output)
break
_cleanup_temp_files(temp_input, temp_output)
continue
# Encoding succeeded - reset failure counter
consecutive_failures = 0
except subprocess.CalledProcessError as e:
# FFmpeg execution failed
error_msg = str(e).split('\n')[0][:100] # First 100 chars of error
if is_smart_mode and method == "CQ":
# In smart mode, log and retry with bitrate
print(f"❌ CQ encode error. Will retry with Bitrate.")
failure_logger.warning(f"{file.name} | CQ error: {error_msg}")
failed_cq_files.append({
'file': file,
'temp_input': temp_input,
'temp_output': temp_output,
'src_width': src_width,
'src_height': src_height,
'res_width': res_width,
'res_height': res_height,
'target_resolution': target_resolution,
'file_cq': file_cq,
'is_tv': is_tv
})
consecutive_failures += 1
if consecutive_failures >= max_consecutive:
print(f"\n⚠️ {max_consecutive} consecutive CQ failures. Moving to Phase 2: Bitrate retry.")
logger.warning(f"{max_consecutive} consecutive CQ failures. Moving to Phase 2.")
break
continue
elif is_forced_cq or is_forced_bitrate:
# In forced mode, skip and log
print(f"{method} encode failed: {error_msg}")
failure_logger.warning(f"{file.name} | {method} error: {error_msg}")
consecutive_failures += 1
if consecutive_failures >= max_consecutive:
print(f"\n{max_consecutive} consecutive failures in forced {method} mode. Stopping.")
logger.error(f"{max_consecutive} consecutive failures. Stopping process.")
_cleanup_temp_files(temp_input, temp_output)
break
_cleanup_temp_files(temp_input, temp_output)
continue
# If we get here, encoding succeeded - save file and log
_save_successful_encoding(
file, temp_input, temp_output, orig_size, out_size,
reduction_ratio, method, src_width, src_height, res_width, res_height,
file_cq, tracker_file, folder, is_tv
)
except Exception as e:
# Unexpected error
error_msg = str(e)[:100]
print(f"❌ Unexpected error: {error_msg}")
failure_logger.warning(f"{file.name} | Unexpected error: {error_msg}")
consecutive_failures += 1
logger.error(f"Unexpected error processing {file.name}: {e}")
_cleanup_temp_files(temp_input, temp_output)
if is_forced_cq or is_forced_bitrate:
if consecutive_failures >= max_consecutive:
print(f"\n{max_consecutive} consecutive failures. Stopping.")
break
else:
if consecutive_failures >= max_consecutive:
print(f"\n⚠️ {max_consecutive} consecutive failures. Moving to Phase 2.")
break
# Phase 2: Retry failed CQ files with Bitrate mode (smart mode only)
if is_smart_mode and failed_cq_files:
print(f"\n{'='*60}")
print(f"📋 PHASE 2: Retrying {len(failed_cq_files)} failed files with Bitrate mode")
print(f"{'='*60}\n")
consecutive_failures = 0
for file_data in failed_cq_files:
file = file_data['file']
temp_input = file_data['temp_input']
temp_output = file_data['temp_output']
try:
print(f"🔄 Retrying: {file.name} with Bitrate")
logger.info(f"Phase 2 Retry: {file.name} with Bitrate mode")
# Clean up old output if it exists
if temp_output.exists():
temp_output.unlink()
# Retry with bitrate
orig_size, out_size, reduction_ratio = run_ffmpeg(
temp_input, temp_output, file_data['file_cq'],
file_data['res_width'], file_data['res_height'],
file_data['src_width'], file_data['src_height'],
filter_flags, audio_config, "Bitrate", bitrate_config
)
# Check if bitrate also failed
if reduction_ratio >= reduction_ratio_threshold:
print(f"⚠️ Bitrate also failed size target ({reduction_ratio:.1%}). Skipping.")
failure_logger.warning(f"{file.name} | Bitrate retry also failed ({reduction_ratio:.1%})")
consecutive_failures += 1
_cleanup_temp_files(temp_input, temp_output)
if consecutive_failures >= max_consecutive:
print(f"\n⚠️ {max_consecutive} consecutive Phase 2 failures. Stopping retries.")
break
continue
# Bitrate succeeded
consecutive_failures = 0
_save_successful_encoding(
file, temp_input, temp_output,
orig_size, out_size, reduction_ratio, "Bitrate",
file_data['src_width'], file_data['src_height'],
file_data['res_width'], file_data['res_height'],
file_data['file_cq'], tracker_file,
folder, file_data['is_tv']
)
except subprocess.CalledProcessError as e:
error_msg = str(e).split('\n')[0][:100]
print(f"❌ Bitrate retry failed: {error_msg}")
failure_logger.warning(f"{file.name} | Bitrate retry error: {error_msg}")
consecutive_failures += 1
logger.error(f"Bitrate retry failed for {file.name}: {e}")
_cleanup_temp_files(temp_input, temp_output)
if consecutive_failures >= max_consecutive:
print(f"\n⚠️ {max_consecutive} consecutive Phase 2 failures. Stopping retries.")
break
except Exception as e:
error_msg = str(e)[:100]
print(f"❌ Unexpected error in Phase 2: {error_msg}")
failure_logger.warning(f"{file.name} | Phase 2 error: {error_msg}")
consecutive_failures += 1
_cleanup_temp_files(temp_input, temp_output)
if consecutive_failures >= max_consecutive:
print(f"\n⚠️ {max_consecutive} consecutive Phase 2 failures. Stopping retries.")
break
print(f"\n{'='*60}")
print("✅ Batch processing complete")
logger.info("Batch processing complete")
def _save_successful_encoding(file, temp_input, temp_output, orig_size, out_size,
reduction_ratio, method, src_width, src_height, res_width, res_height,
file_cq, tracker_file, folder, is_tv):
"""Helper function to save successfully encoded files."""
dest_file = file.parent / temp_output.name
shutil.move(temp_output, dest_file)
print(f"🚚 Moved {temp_output.name}{dest_file.name}")
logger.info(f"Moved {temp_output.name}{dest_file.name}")
# Classify file type based on folder
folder_parts = [p.lower() for p in folder.parts]
if "tv" in folder_parts:
f_type = "tv"
tv_index = folder_parts.index("tv")
show = folder.parts[tv_index + 1] if len(folder.parts) > tv_index + 1 else "Unknown"
elif "anime" in folder_parts:
f_type = "anime"
anime_index = folder_parts.index("anime")
show = folder.parts[anime_index + 1] if len(folder.parts) > anime_index + 1 else "Unknown"
else:
f_type = "movie"
show = "N/A"
orig_size_mb = round(orig_size / 1e6, 2)
proc_size_mb = round(out_size / 1e6, 2)
percentage = round(proc_size_mb / orig_size_mb * 100, 1)
# Get audio stream count for tracking
try:
audio_streams = get_audio_streams(temp_input)
audio_stream_count = len(audio_streams)
except:
audio_stream_count = 0
# Format resolutions for tracking
src_resolution = f"{src_width}x{src_height}"
target_res = f"{res_width}x{res_height}"
cq_str = str(file_cq) if method == "CQ" else "N/A"
with open(tracker_file, "a", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow([
f_type, show, dest_file.name, orig_size_mb, proc_size_mb, percentage,
src_resolution, target_res, audio_stream_count, cq_str, method
])
# Enhanced logging with all conversion details
logger.info(f"\n✅ CONVERSION COMPLETE: {dest_file.name}")
logger.info(f" Type: {f_type.upper()} | Show: {show}")
logger.info(f" Size: {orig_size_mb}MB → {proc_size_mb}MB ({percentage}% of original, {100-percentage:.1f}% reduction)")
logger.info(f" Method: {method} | Status: SUCCESS")
print(f"📝 Logged conversion: {dest_file.name} ({percentage}%), method={method}")
try:
temp_input.unlink()
file.unlink()
logger.info(f"Deleted original and processing copy for {file.name}")
except Exception as e:
print(f"⚠️ Could not delete files: {e}")
logger.warning(f"Could not delete files: {e}")

View File

69
core/video_handler.py Normal file
View File

@ -0,0 +1,69 @@
# core/video_handler.py
"""Video resolution detection and encoding logic."""
import subprocess
from pathlib import Path
from core.logger_helper import setup_logger
logger = setup_logger(Path(__file__).parent.parent / "logs")
def get_source_resolution(input_file: Path) -> tuple:
"""
Get source video resolution (width, height).
Returns tuple: (width, height)
"""
try:
cmd = [
"ffprobe", "-v", "error",
"-select_streams", "v:0",
"-show_entries", "stream=width,height",
"-of", "default=noprint_wrappers=1:nokey=1:noprint_wrappers=1",
str(input_file)
]
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
lines = result.stdout.strip().split("\n")
width = int(lines[0]) if len(lines) > 0 else 1920
height = int(lines[1]) if len(lines) > 1 else 1080
logger.info(f"Source resolution detected: {width}x{height}")
return (width, height)
except Exception as e:
logger.warning(f"Failed to detect source resolution: {e}. Defaulting to 1920x1080")
return (1920, 1080)
def determine_target_resolution(src_width: int, src_height: int, explicit_resolution: str = None) -> tuple:
"""
Determine target resolution based on source and explicit override.
Returns tuple: (res_width, res_height, target_resolution_label)
Logic:
If explicit_resolution specified: use it
Else:
- If source > 1080p: scale to 1080p
- If source <= 1080p: preserve source resolution
"""
if explicit_resolution:
# User explicitly specified resolution - always use it
if explicit_resolution == "1080":
return (1920, 1080, "1080")
elif explicit_resolution == "720":
return (1280, 720, "720")
else: # 480
return (854, 480, "480")
else:
# No explicit resolution - use smart defaults
if src_height > 1080:
# Scale down anything above 1080p to 1080p
logger.info(f"Source {src_width}x{src_height} detected. Scaling to 1080p.")
return (1920, 1080, "1080")
else:
# Preserve source resolution (480p, 720p, 1080p, etc.)
if src_height <= 720:
logger.info(f"Source {src_width}x{src_height} (<=720p). Preserving source resolution.")
return (src_width, src_height, "720")
else:
logger.info(f"Source {src_width}x{src_height} (<=1080p). Preserving source resolution.")
return (src_width, src_height, "1080")

832
gui_path_manager.py Normal file
View File

@ -0,0 +1,832 @@
#!/usr/bin/env python3
"""
GUI Path Manager for Batch Video Transcoder
Allows easy browsing of folders and appending to paths.txt with encoding options.
"""
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from pathlib import Path
import os
import subprocess
import re
import json
from core.config_helper import load_config_xml
from core.logger_helper import setup_logger
logger = setup_logger(Path(__file__).parent / "logs")
class PathManagerGUI:
def __init__(self, root):
self.root = root
self.root.title("Batch Transcoder - Path Manager")
self.root.geometry("1100x700")
# Load config
config_path = Path(__file__).parent / "config.xml"
self.config = load_config_xml(config_path)
self.path_mappings = self.config.get("path_mappings", {})
# Paths file
self.paths_file = Path(__file__).parent / "paths.txt"
self.transcode_bat = Path(__file__).parent / "transcode.bat"
# Current selected folder
self.selected_folder = None
self.current_category = None
self.recently_added = None # Track recently added folder for highlighting
self.status_timer = None # Track status message timer
self.added_folders = set() # Folders that are in paths.txt
# Cache for folder data - split per category
self.cache_dir = Path(__file__).parent / ".cache"
self.cache_dir.mkdir(exist_ok=True)
self.folder_cache = {} # Only current category in memory: {folder_path: size}
self.scan_in_progress = False
self.scanned_categories = set() # Track which categories have been scanned
# Lazy loading
self.all_folders = [] # All folders for current category
self.loaded_items = 0 # How many items are currently loaded
self.items_per_batch = 100 # Load 100 items at a time
# Load existing paths
self._load_existing_paths()
# Build UI
self._build_ui()
# Handle window close
self.root.protocol("WM_DELETE_WINDOW", self._on_closing)
def _build_ui(self):
"""Build the GUI layout."""
# Top frame for category selection and transcode launcher
top_frame = ttk.Frame(self.root)
top_frame.pack(fill=tk.X, padx=10, pady=10)
left_top = ttk.Frame(top_frame)
left_top.pack(side=tk.LEFT, fill=tk.X, expand=True)
ttk.Label(left_top, text="Category:").pack(side=tk.LEFT, padx=5)
self.category_var = tk.StringVar(value="tv")
categories = ["tv", "anime", "movies"]
for cat in categories:
ttk.Radiobutton(
left_top, text=cat.upper(), variable=self.category_var,
value=cat, command=self._on_category_change
).pack(side=tk.LEFT, padx=5)
ttk.Button(left_top, text="Refresh", command=self._refresh_with_cache_clear).pack(side=tk.LEFT, padx=5)
# Right side of top frame - transcode launcher
right_top = ttk.Frame(top_frame)
right_top.pack(side=tk.RIGHT)
if self.transcode_bat.exists():
ttk.Button(
right_top, text="▶ Run transcode.bat", command=self._run_transcode
).pack(side=tk.RIGHT, padx=5)
# Main content frame
main_frame = ttk.Frame(self.root)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Left side - folder tree
left_frame = ttk.LabelFrame(main_frame, text="Folders (sorted by size)")
left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))
# Treeview for folders with add button column
self.tree = ttk.Treeview(left_frame, columns=("size", "add", "remove"), height=20)
self.tree.column("#0", width=180)
self.tree.column("size", width=80)
self.tree.column("add", width=50)
self.tree.column("remove", width=50)
self.tree.heading("#0", text="Folder Name")
self.tree.heading("size", text="Size")
self.tree.heading("add", text="Add")
self.tree.heading("remove", text="Remove")
scrollbar = ttk.Scrollbar(left_frame, orient=tk.VERTICAL, command=self._on_scrollbar)
self.tree.configure(yscroll=scrollbar.set)
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# Configure tags for folder status
self.tree.tag_configure("added", background="#90EE90") # Light green for added
self.tree.tag_configure("not_added", background="white") # White for not added
self.tree.tag_configure("recently_added", background="#FFD700") # Gold for recently added
self.tree.bind("<Double-1>", self._on_folder_expand)
self.tree.bind("<<TreeviewSelect>>", self._on_folder_select)
self.tree.bind("<Button-1>", self._on_tree_click)
# Right side - options and preview
right_frame = ttk.LabelFrame(main_frame, text="Encoding Options & Preview")
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0))
# Mode selection
mode_frame = ttk.LabelFrame(right_frame, text="Mode (--m)")
mode_frame.pack(fill=tk.X, padx=5, pady=5)
self.mode_var = tk.StringVar(value="default")
for mode in ["default", "cq", "bitrate"]:
ttk.Radiobutton(mode_frame, text=mode, variable=self.mode_var, value=mode,
command=self._update_preview).pack(anchor=tk.W, padx=5)
# Resolution selection
res_frame = ttk.LabelFrame(right_frame, text="Resolution (--r)")
res_frame.pack(fill=tk.X, padx=5, pady=5)
self.resolution_var = tk.StringVar(value="none")
for res in ["none", "480", "720", "1080"]:
label = "Auto" if res == "none" else res + "p"
ttk.Radiobutton(res_frame, text=label, variable=self.resolution_var, value=res,
command=self._update_preview).pack(anchor=tk.W, padx=5)
# CQ value
cq_frame = ttk.LabelFrame(right_frame, text="CQ Value (--cq, optional)")
cq_frame.pack(fill=tk.X, padx=5, pady=5)
self.cq_var = tk.StringVar(value="")
cq_entry = ttk.Entry(cq_frame, textvariable=self.cq_var, width=10)
cq_entry.pack(anchor=tk.W, padx=5, pady=3)
cq_entry.bind("<KeyRelease>", lambda e: self._update_preview())
# Preview frame
preview_frame = ttk.LabelFrame(right_frame, text="Command Preview")
preview_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self.preview_text = tk.Text(preview_frame, height=8, width=40, wrap=tk.WORD, bg="lightgray")
self.preview_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self.preview_text.config(state=tk.DISABLED)
# Bottom frame - action buttons and status
bottom_frame = ttk.Frame(self.root)
bottom_frame.pack(fill=tk.X, padx=10, pady=10)
button_frame = ttk.Frame(bottom_frame)
button_frame.pack(side=tk.LEFT)
ttk.Button(button_frame, text="View paths.txt", command=self._view_paths_file).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="Clear paths.txt", command=self._clear_paths_file).pack(side=tk.LEFT, padx=5)
# Status label (for silent feedback)
self.status_label = ttk.Label(bottom_frame, text="", foreground="green")
self.status_label.pack(side=tk.LEFT, padx=10)
# Load cache and populate initial category
self._load_cache()
self._refresh_folders(use_cache=True)
# Only scan once per category on first view
if self.current_category not in self.scanned_categories:
self.root.after(100, self._scan_folders_once)
def _on_category_change(self):
"""Handle category radio button change."""
self.current_category = self.category_var.get()
# Load cache for this category
self._load_cache()
# Show cached data first
self._refresh_folders(use_cache=True)
# Only scan once per category on first view
if self.current_category not in self.scanned_categories:
self.root.after(100, self._scan_folders_once)
def _load_cache(self):
"""Load folder cache for current category from disk (lazy)."""
category = self.category_var.get()
cache_file = self.cache_dir / f".cache_{category}.json"
self.folder_cache.clear()
# Don't fully load cache yet - just verify it exists
if not cache_file.exists():
logger.info(f"No cache file for {category}")
else:
logger.info(f"Cache file exists for {category}")
def _parse_cache_lazily(self, limit=None):
"""Parse cache file lazily and return folders."""
category = self.category_var.get()
cache_file = self.cache_dir / f".cache_{category}.json"
folders = []
if cache_file.exists():
try:
with open(cache_file, "r", encoding="utf-8") as f:
cache_dict = json.load(f)
# Convert to list and sort
for folder_path_str, size in cache_dict.items():
folder_path = Path(folder_path_str)
if folder_path.exists():
folders.append((folder_path.name, folder_path, size))
# Early exit if limit reached
if limit and len(folders) >= limit:
break
# Sort by size descending (only what we loaded)
folders.sort(key=lambda x: x[2], reverse=True)
except Exception as e:
logger.error(f"Failed to parse cache: {e}")
return folders
def _save_cache(self):
"""Save current category's folder cache to disk."""
category = self.category_var.get()
cache_file = self.cache_dir / f".cache_{category}.json"
try:
with open(cache_file, "w", encoding="utf-8") as f:
json.dump(self.folder_cache, f, indent=2)
except Exception as e:
logger.error(f"Failed to save {category} cache: {e}")
def _refresh_with_cache_clear(self):
"""Refresh and clear cache to force full scan."""
category = self.category_var.get()
cache_file = self.cache_dir / f".cache_{category}.json"
# Delete cache file for this category
if cache_file.exists():
cache_file.unlink()
self.folder_cache.clear()
self.scanned_categories.discard(category) # Reset so it will scan again
self._refresh_folders(use_cache=False)
def _scan_folders_once(self):
"""Scan folders once per category on first load."""
if self.scan_in_progress:
return
category = self.category_var.get()
if category in self.scanned_categories:
return # Already scanned this category
self.scan_in_progress = True
try:
category_mapping = {
"tv": "P:\\tv",
"anime": "P:\\anime",
"movies": "P:\\movies"
}
base_key = category_mapping.get(category)
if not base_key or base_key not in self.path_mappings:
return
base_path = Path(base_key)
if not base_path.exists():
return
# Scan folders and update cache
new_cache = {}
for entry in os.scandir(base_path):
if entry.is_dir(follow_symlinks=False):
size = self._get_folder_size(Path(entry))
new_cache[str(Path(entry))] = size
# Update cache and save
self.folder_cache = new_cache
self._save_cache()
self.scanned_categories.add(category)
# Update UI if still on same category
if self.category_var.get() == category:
self._refresh_folders(use_cache=True)
finally:
self.scan_in_progress = False
def _scan_folders_background(self):
"""Scan folders in background and update cache."""
if self.scan_in_progress:
return
self.scan_in_progress = True
try:
category = self.category_var.get()
category_mapping = {
"tv": "P:\\tv",
"anime": "P:\\anime",
"movies": "P:\\movies"
}
base_key = category_mapping.get(category)
if not base_key or base_key not in self.path_mappings:
return
base_path = Path(base_key)
if not base_path.exists():
return
# Scan folders and update cache
new_cache = {}
for entry in os.scandir(base_path):
if entry.is_dir(follow_symlinks=False):
size = self._get_folder_size(Path(entry))
new_cache[str(Path(entry))] = size
# Update cache and save
self.folder_cache[category] = new_cache
self._save_cache()
# Update UI if still on same category
if self.category_var.get() == category:
self._refresh_folders(use_cache=True)
finally:
self.scan_in_progress = False
# Schedule next continuous scan
self.background_scan_timer = self.root.after(
self.background_scan_interval,
self._continuous_background_scan
)
# Schedule next continuous scan
self.background_scan_timer = self.root.after(
self.background_scan_interval,
self._continuous_background_scan
)
def _load_existing_paths(self):
"""Load existing paths from paths.txt and extract folder paths."""
self.added_folders.clear()
if not self.paths_file.exists():
return
try:
with open(self.paths_file, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line:
continue
# Extract the path (last argument in the command)
# Format: --m mode --r res --cq val "path" or just "path"
# Find all quoted strings
matches = re.findall(r'"([^"]*)"', line)
if matches:
# Last quoted string is the path
path = matches[-1]
self.added_folders.add(path)
except Exception as e:
logger.error(f"Failed to load existing paths: {e}")
def _get_folder_size(self, path: Path) -> int:
"""Calculate total size of folder in bytes."""
total = 0
try:
for entry in os.scandir(path):
if entry.is_file(follow_symlinks=False):
total += entry.stat().st_size
elif entry.is_dir(follow_symlinks=False):
total += self._get_folder_size(Path(entry))
except PermissionError:
pass
return total
def _format_size(self, bytes_size: int) -> str:
"""Format bytes to human readable size."""
for unit in ["B", "KB", "MB", "GB", "TB"]:
if bytes_size < 1024:
return f"{bytes_size:.1f} {unit}"
bytes_size /= 1024
return f"{bytes_size:.1f} PB"
def _refresh_folders(self, use_cache=False):
"""Refresh the folder tree from cache or disk."""
# Clear existing items
for item in self.tree.get_children():
self.tree.delete(item)
self.all_folders = []
self.loaded_items = 0
category = self.category_var.get()
# Map category to path mapping key
category_mapping = {
"tv": "P:\\tv",
"anime": "P:\\anime",
"movies": "P:\\movies"
}
base_key = category_mapping.get(category)
if not base_key or base_key not in self.path_mappings:
messagebox.showwarning("Info", f"No mapping found for {category}")
return
base_path = Path(base_key)
# Check if path exists
if not base_path.exists():
messagebox.showerror("Error", f"Path not found: {base_path}")
return
# Get folders from cache or disk
if use_cache:
# Parse cache lazily - only load what we need initially
folders = self._parse_cache_lazily(limit=None) # Get all but parse efficiently
else:
# Scan from disk
folders = []
try:
for entry in os.scandir(base_path):
if entry.is_dir(follow_symlinks=False):
size = self._get_folder_size(Path(entry))
folders.append((entry.name, Path(entry), size))
except PermissionError:
messagebox.showerror("Error", f"Permission denied accessing {base_path}")
return
# Update cache with fresh scan
cache_dict = {str(f[1]): f[2] for f in folders}
self.folder_cache = cache_dict
self._save_cache()
# Sort by size descending
folders.sort(key=lambda x: x[2], reverse=True)
# Store all folders and load first batch only
self.all_folders = folders
self._load_more_items()
def _on_folder_expand(self, event):
"""Handle folder double-click to show contents."""
selection = self.tree.selection()
if not selection:
return
item = selection[0]
tags = self.tree.item(item, "tags")
if not tags:
return
folder_path = Path(tags[0])
# Check if already expanded
if self.tree.get_children(item):
# Toggle: remove children
for child in self.tree.get_children(item):
self.tree.delete(child)
else:
# Add file/folder contents
try:
entries = []
for entry in os.scandir(folder_path):
if entry.is_file():
size = entry.stat().st_size
entries.append((entry.name, "File", size))
elif entry.is_dir():
size = self._get_folder_size(Path(entry))
entries.append((entry.name, "Folder", size))
# Sort by size descending
entries.sort(key=lambda x: x[2], reverse=True)
for name, type_str, size in entries:
size_str = self._format_size(size)
self.tree.insert(item, "end", text=f"[{type_str}] {name}", values=(size_str,))
except PermissionError:
messagebox.showerror("Error", f"Permission denied accessing {folder_path}")
def _on_folder_select(self, event):
"""Handle folder selection to update preview."""
selection = self.tree.selection()
if not selection:
return
item = selection[0]
tags = self.tree.item(item, "tags")
if tags:
self.selected_folder = tags[0]
self._update_preview()
def _on_tree_click(self, event):
"""Handle click on '+' or '-' button in add column."""
item = self.tree.identify("item", event.x, event.y)
column = self.tree.identify_column(event.x) # Only takes x coordinate
# Column #2 is the "add" column (columns are #0=name, #1=size, #2=add, #3=remove)
if item and column == "#2":
tags = self.tree.item(item, "tags")
if tags:
folder_path = tags[0]
values = self.tree.item(item, "values")
if len(values) > 1:
button_text = values[1] # Get button text
if "[+]" in button_text:
# Immediately update UI for snappy response
size_val = values[0]
self.tree.item(item, values=(size_val, "", "[-]"), tags=(folder_path, "added"))
# Add to paths.txt asynchronously
self.selected_folder = folder_path
self.root.after(0, self._add_to_paths_file_async, folder_path)
# Column #3 is the "remove" column
elif item and column == "#3":
tags = self.tree.item(item, "tags")
if tags:
folder_path = tags[0]
# Immediately update UI for snappy response
values = self.tree.item(item, "values")
size_val = values[0]
self.tree.item(item, values=(size_val, "[+]", ""), tags=(folder_path, "not_added"))
# Remove from paths.txt asynchronously
self.root.after(0, self._remove_from_paths_file_async, folder_path)
def _add_to_paths_file_async(self, folder_path):
"""Add to paths.txt without blocking UI."""
self.selected_folder = folder_path
self._add_to_paths_file()
# Silently reload in background
self._load_existing_paths()
def _remove_from_paths_file_async(self, folder_path):
"""Remove from paths.txt without blocking UI."""
self._remove_from_paths_file(folder_path)
def _update_preview(self):
"""Update the command preview."""
if not self.selected_folder:
preview_text = "No folder selected"
else:
folder_path = self.selected_folder
# Build command
cmd_parts = ['py main.py']
# Add mode if not default
mode = self.mode_var.get()
if mode != "default":
cmd_parts.append(f'--m {mode}')
# Add resolution if specified
resolution = self.resolution_var.get()
if resolution != "none":
cmd_parts.append(f'--r {resolution}')
# Add CQ if specified
cq = self.cq_var.get().strip()
if cq:
cmd_parts.append(f'--cq {cq}')
# Add path
cmd_parts.append(f'"{folder_path}"')
preview_text = " ".join(cmd_parts)
self.preview_text.config(state=tk.NORMAL)
self.preview_text.delete("1.0", tk.END)
self.preview_text.insert("1.0", preview_text)
self.preview_text.config(state=tk.DISABLED)
def _add_to_paths_file(self):
"""Append the current command to paths.txt."""
if not self.selected_folder:
messagebox.showwarning("Warning", "Please select a folder first")
return
folder_path = self.selected_folder
# Check if already in file
if folder_path in self.added_folders:
self._show_status(f"Already added: {Path(folder_path).name}")
return
# Build command line - start fresh
cmd_parts = []
# Add mode if not default
mode = self.mode_var.get()
if mode != "default":
cmd_parts.append(f'--m {mode}')
# Add resolution if specified
resolution = self.resolution_var.get()
if resolution != "none":
cmd_parts.append(f'--r {resolution}')
# Add CQ if specified
cq = self.cq_var.get().strip()
if cq:
cmd_parts.append(f'--cq {cq}')
# Add folder path
cmd_parts.append(f'"{folder_path}"')
line = " ".join(cmd_parts)
# Append to paths.txt
try:
# Check if file exists and has content
if self.paths_file.exists() and self.paths_file.stat().st_size > 0:
# Read last character to check if it ends with newline
with open(self.paths_file, "rb") as f:
f.seek(-1, 2) # Seek to last byte
last_char = f.read(1)
needs_newline = last_char != b'\n'
else:
needs_newline = False
# Write to file
with open(self.paths_file, "a", encoding="utf-8") as f:
if needs_newline:
f.write("\n")
f.write(line + "\n")
# Add to tracked set
self.added_folders.add(folder_path)
# Silent success - show status label instead of popup
self.recently_added = folder_path
self._show_status(f"✓ Added: {Path(folder_path).name}")
logger.info(f"Added to paths.txt: {line}")
# Clear timer if exists
if self.status_timer:
self.root.after_cancel(self.status_timer)
# Clear status after 3 seconds
self.status_timer = self.root.after(3000, self._clear_status)
except Exception as e:
messagebox.showerror("Error", f"Failed to write to paths.txt: {e}")
logger.error(f"Failed to write to paths.txt: {e}")
def _remove_from_paths_file(self, folder_path):
"""Remove a folder from paths.txt."""
if not self.paths_file.exists():
messagebox.showwarning("Warning", "paths.txt does not exist")
return
try:
with open(self.paths_file, "r", encoding="utf-8") as f:
lines = f.readlines()
# Filter out lines containing this folder path
new_lines = []
found = False
for line in lines:
if f'"{folder_path}"' in line or f"'{folder_path}'" in line:
found = True
else:
new_lines.append(line)
if not found:
messagebox.showwarning("Warning", "Path not found in paths.txt")
return
# Write back
with open(self.paths_file, "w", encoding="utf-8") as f:
f.writelines(new_lines)
# Remove from tracked set
self.added_folders.discard(folder_path)
self._show_status(f"✓ Removed: {Path(folder_path).name}")
logger.info(f"Removed from paths.txt: {folder_path}")
# Clear timer if exists
if self.status_timer:
self.root.after_cancel(self.status_timer)
# Clear status after 3 seconds
self.status_timer = self.root.after(3000, self._clear_status)
except Exception as e:
messagebox.showerror("Error", f"Failed to remove from paths.txt: {e}")
logger.error(f"Failed to remove from paths.txt: {e}")
def _show_status(self, message):
"""Show status message in label."""
self.status_label.config(text=message, foreground="green")
def _clear_status(self):
"""Clear status message after delay."""
self.status_label.config(text="")
self.status_timer = None
def _run_transcode(self):
"""Launch transcode.bat in a new command window."""
if not self.transcode_bat.exists():
messagebox.showerror("Error", f"transcode.bat not found at {self.transcode_bat}")
return
try:
# Launch in new cmd window
subprocess.Popen(
['cmd', '/c', f'"{self.transcode_bat}"'],
cwd=str(self.transcode_bat.parent),
creationflags=subprocess.CREATE_NEW_CONSOLE
)
logger.info("Launched transcode.bat")
except Exception as e:
messagebox.showerror("Error", f"Failed to launch transcode.bat: {e}")
logger.error(f"Failed to launch transcode.bat: {e}")
def _view_paths_file(self):
"""Open paths.txt in a new window."""
if not self.paths_file.exists():
messagebox.showinfo("Info", "paths.txt does not exist yet")
return
try:
with open(self.paths_file, "r", encoding="utf-8") as f:
content = f.read()
# Create new window
view_window = tk.Toplevel(self.root)
view_window.title("paths.txt")
view_window.geometry("800x400")
text_widget = tk.Text(view_window, wrap=tk.WORD)
text_widget.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
text_widget.insert("1.0", content)
# Add close button
ttk.Button(view_window, text="Close", command=view_window.destroy).pack(pady=5)
except Exception as e:
messagebox.showerror("Error", f"Failed to read paths.txt: {e}")
def _clear_paths_file(self):
"""Clear the paths.txt file."""
if not self.paths_file.exists():
messagebox.showinfo("Info", "paths.txt does not exist")
return
if messagebox.askyesno("Confirm", "Are you sure you want to clear paths.txt?"):
try:
self.paths_file.write_text("", encoding="utf-8")
messagebox.showinfo("Success", "paths.txt has been cleared")
logger.info("paths.txt cleared")
except Exception as e:
messagebox.showerror("Error", f"Failed to clear paths.txt: {e}")
def _on_closing(self):
"""Handle window closing - cleanup timers."""
self.root.destroy()
def _on_scrollbar(self, *args):
"""Handle scrollbar movement - load more items when scrolling."""
self.tree.yview(*args)
# Check if we need to load more items
if self.all_folders and self.loaded_items < len(self.all_folders):
# Get scroll position
first_visible = self.tree.yview()[0]
last_visible = self.tree.yview()[1]
# If we're past 70% scrolled, load more
if last_visible > 0.7:
self._load_more_items()
def _load_more_items(self):
"""Load next batch of items into tree."""
start = self.loaded_items
end = min(start + self.items_per_batch, len(self.all_folders))
for i in range(start, end):
folder_name, folder_path, size = self.all_folders[i]
size_str = self._format_size(size)
folder_path_str = str(folder_path)
# Determine button and tag
if folder_path_str in self.added_folders:
add_btn = ""
remove_btn = "[-]"
tag = "added"
else:
add_btn = "[+]"
remove_btn = ""
tag = "not_added"
self.tree.insert("", "end", text=folder_name, values=(size_str, add_btn, remove_btn),
tags=(folder_path_str, tag))
self.loaded_items = end
def main():
root = tk.Tk()
app = PathManagerGUI(root)
root.mainloop()
if __name__ == "__main__":
main()

View File

@ -4191,3 +4191,902 @@
2025-12-31 11:44:03 [INFO] - Stream #2: 2ch→2ch, src=und, detected=65kbps, action=COPY (preserve), target=65kbps
2025-12-31 11:44:03 [INFO] Running CQ encode: How the Grinch Stole Christmas (2000) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv
2025-12-31 11:52:44 [INFO] 📦 Original: 5626.25 MB → Encoded: 3268.49 MB (58.1% of original)
2025-12-31 11:53:12 [INFO] Moved How the Grinch Stole Christmas (2000) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv → How the Grinch Stole Christmas (2000) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv
2025-12-31 11:53:12 [INFO] Tracked conversion: How the Grinch Stole Christmas (2000) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv, 5626.25MB → 3268.49MB (58.1%), method=CQ
2025-12-31 11:53:13 [INFO] Deleted original and processing copy for How the Grinch Stole Christmas (2000) x265 AAC 5.1 Bluray-1080p Tigole.mkv
2025-12-31 12:06:59 [INFO] Processing: Oppenheimer (2023) x265 AAC 5.1 Bluray-1080p Tigole.mkv
2025-12-31 12:08:20 [INFO] Copied Oppenheimer (2023) x265 AAC 5.1 Bluray-1080p Tigole.mkv → Oppenheimer (2023) x265 AAC 5.1 Bluray-1080p Tigole.mkv
2025-12-31 12:08:20 [INFO] Source resolution detected: 1920x1080
2025-12-31 12:08:20 [INFO] Source 1920x1080 (<=1080p). Preserving source resolution.
2025-12-31 12:08:20 [INFO] Source 1920x1080 (<=1080p). Preserving source resolution.
2025-12-31 12:08:26 [INFO]
🧩 ENCODE SETTINGS
2025-12-31 12:08:26 [INFO] Video:
2025-12-31 12:08:26 [INFO] • Source Resolution: 1920x1080
2025-12-31 12:08:26 [INFO] • Target Resolution: 1920x1080
2025-12-31 12:08:26 [INFO] • Encoder: av1_nvenc (preset p1, pix_fmt p010le)
2025-12-31 12:08:26 [INFO] • Scale Filter: lanczos
2025-12-31 12:08:26 [INFO] • Encode Method: CQ
2025-12-31 12:08:26 [INFO] • CQ Value: 32
2025-12-31 12:08:26 [INFO] Audio Streams (1 detected):
2025-12-31 12:08:26 [INFO] - Stream #1: 6ch→6ch | Lang: und | Detected: 418kbps | Action: ENCODE | Target: 384kbps
2025-12-31 12:08:26 [INFO] Running CQ encode: Oppenheimer (2023) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv
2025-12-31 12:21:02 [INFO]
📊 ENCODE RESULTS:
2025-12-31 12:21:02 [INFO] Original Size: 9344.87 MB
2025-12-31 12:21:02 [INFO] Encoded Size: 3040.30 MB
2025-12-31 12:21:02 [INFO] Reduction: 32.5% of original (67.5% saved)
2025-12-31 12:21:02 [INFO] Resolution: 1920x1080 → 1920x1080
2025-12-31 12:21:02 [INFO] Audio Streams: 1 streams processed
2025-12-31 12:21:28 [INFO] Moved Oppenheimer (2023) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv → Oppenheimer (2023) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv
2025-12-31 12:21:39 [INFO]
✅ CONVERSION COMPLETE: Oppenheimer (2023) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv
2025-12-31 12:21:39 [INFO] Type: MOVIE | Show: N/A
2025-12-31 12:21:39 [INFO] Size: 9344.87MB → 3040.3MB (32.5% of original, 67.5% reduction)
2025-12-31 12:21:39 [INFO] Method: CQ | Status: SUCCESS
2025-12-31 12:21:40 [INFO] Deleted original and processing copy for Oppenheimer (2023) x265 AAC 5.1 Bluray-1080p Tigole.mkv
2025-12-31 12:21:40 [INFO] Processing: Innovations in Film - 65mm Black-and-White Film in Oppenheimer.mkv
2025-12-31 12:21:41 [INFO] Copied Innovations in Film - 65mm Black-and-White Film in Oppenheimer.mkv → Innovations in Film - 65mm Black-and-White Film in Oppenheimer.mkv
2025-12-31 12:21:41 [INFO] Source resolution detected: 1920x1080
2025-12-31 12:21:41 [INFO] Source 1920x1080 (<=1080p). Preserving source resolution.
2025-12-31 12:21:41 [INFO] Source 1920x1080 (<=1080p). Preserving source resolution.
2025-12-31 12:21:41 [INFO]
🧩 ENCODE SETTINGS
2025-12-31 12:21:41 [INFO] Video:
2025-12-31 12:21:41 [INFO] • Source Resolution: 1920x1080
2025-12-31 12:21:41 [INFO] • Target Resolution: 1920x1080
2025-12-31 12:21:41 [INFO] • Encoder: av1_nvenc (preset p1, pix_fmt p010le)
2025-12-31 12:21:41 [INFO] • Scale Filter: lanczos
2025-12-31 12:21:41 [INFO] • Encode Method: CQ
2025-12-31 12:21:41 [INFO] • CQ Value: 32
2025-12-31 12:21:41 [INFO] Audio Streams (1 detected):
2025-12-31 12:21:41 [INFO] - Stream #1: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps
2025-12-31 12:21:41 [INFO] Running CQ encode: Innovations in Film - 65mm Black-and-White Film in Oppenheimer -EHX.mkv
2025-12-31 12:22:02 [INFO]
📊 ENCODE RESULTS:
2025-12-31 12:22:02 [INFO] Original Size: 83.83 MB
2025-12-31 12:22:02 [INFO] Encoded Size: 110.80 MB
2025-12-31 12:22:02 [INFO] Reduction: 132.2% of original (-32.2% saved)
2025-12-31 12:22:02 [INFO] Resolution: 1920x1080 → 1920x1080
2025-12-31 12:22:02 [INFO] Audio Streams: 1 streams processed
2025-12-31 12:22:02 [WARNING] CQ encode failed target (132.2%). Switching to Bitrate for remaining files.
2025-12-31 12:22:02 [INFO]
🧩 ENCODE SETTINGS
2025-12-31 12:22:02 [INFO] Video:
2025-12-31 12:22:02 [INFO] • Source Resolution: 1920x1080
2025-12-31 12:22:02 [INFO] • Target Resolution: 1920x1080
2025-12-31 12:22:02 [INFO] • Encoder: av1_nvenc (preset p1, pix_fmt p010le)
2025-12-31 12:22:02 [INFO] • Scale Filter: lanczos
2025-12-31 12:22:02 [INFO] • Encode Method: Bitrate
2025-12-31 12:22:02 [INFO] • Bitrate: 1500k, Max: 1750k
2025-12-31 12:22:02 [INFO] Audio Streams (1 detected):
2025-12-31 12:22:02 [INFO] - Stream #1: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps
2025-12-31 12:22:02 [INFO] Running Bitrate encode: Innovations in Film - 65mm Black-and-White Film in Oppenheimer -EHX.mkv
2025-12-31 12:22:23 [INFO]
📊 ENCODE RESULTS:
2025-12-31 12:22:23 [INFO] Original Size: 83.83 MB
2025-12-31 12:22:23 [INFO] Encoded Size: 110.46 MB
2025-12-31 12:22:23 [INFO] Reduction: 131.8% of original (-31.8% saved)
2025-12-31 12:22:23 [INFO] Resolution: 1920x1080 → 1920x1080
2025-12-31 12:22:23 [INFO] Audio Streams: 1 streams processed
2025-12-31 12:22:23 [ERROR] Bitrate encode failed target (131.8%). Stopping process.
{"timestamp": "2025-12-31T17:29:47Z", "level": "ERROR", "message": "Folder not found: P:\\movies\\Pacific Rim (2013)'", "module": "process_manager", "funcName": "process_folder", "line": 32}
{"timestamp": "2025-12-31T17:29:54Z", "level": "INFO", "message": "Processing: Pacific Rim (2013) x265 AAC 7.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 84}
{"timestamp": "2025-12-31T17:30:41Z", "level": "INFO", "message": "Copied Pacific Rim (2013) x265 AAC 7.1 Bluray-1080p Tigole.mkv → Pacific Rim (2013) x265 AAC 7.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 89}
{"timestamp": "2025-12-31T17:30:42Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T17:30:42Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T17:30:42Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 110}
{"timestamp": "2025-12-31T17:30:51Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T17:30:51Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T17:30:51Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T17:30:51Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T17:30:51Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T17:30:51Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T17:30:51Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T17:30:51Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T17:30:51Z", "level": "INFO", "message": " Audio Streams (2 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T17:30:51Z", "level": "INFO", "message": " - Stream #1: 8ch→6ch | Lang: und | Detected: 623kbps | Action: ENCODE | Target: 448kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T17:30:51Z", "level": "INFO", "message": " - Stream #2: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T17:30:51Z", "level": "INFO", "message": "Running CQ encode: Pacific Rim (2013) x265 AAC 7.1 Bluray-1080p Tigole -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T17:42:36Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T17:42:36Z", "level": "INFO", "message": " Original Size: 5624.98 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T17:42:36Z", "level": "INFO", "message": " Encoded Size: 4029.53 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T17:42:36Z", "level": "INFO", "message": " Reduction: 71.6% of original (28.4% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T17:42:36Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T17:42:36Z", "level": "INFO", "message": " Audio Streams: 2 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T17:42:37Z", "level": "INFO", "message": "Processing: Behind the Scenes - Drift Space.mkv", "module": "process_manager", "funcName": "process_folder", "line": 84}
{"timestamp": "2025-12-31T17:42:38Z", "level": "INFO", "message": "Copied Behind the Scenes - Drift Space.mkv → Behind the Scenes - Drift Space.mkv", "module": "process_manager", "funcName": "process_folder", "line": 89}
{"timestamp": "2025-12-31T17:42:38Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T17:42:38Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T17:42:38Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 110}
{"timestamp": "2025-12-31T17:42:38Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T17:42:38Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T17:42:38Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T17:42:38Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T17:42:38Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T17:42:38Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T17:42:38Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T17:42:38Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T17:42:38Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T17:42:38Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T17:42:38Z", "level": "INFO", "message": "Running CQ encode: Behind the Scenes - Drift Space -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T17:42:54Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T17:42:54Z", "level": "INFO", "message": " Original Size: 64.77 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T17:42:54Z", "level": "INFO", "message": " Encoded Size: 79.36 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T17:42:54Z", "level": "INFO", "message": " Reduction: 122.5% of original (-22.5% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T17:42:54Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T17:42:54Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T17:42:54Z", "level": "INFO", "message": "Processing: Behind the Scenes - The Digital Artistry of Pacific Rim.mkv", "module": "process_manager", "funcName": "process_folder", "line": 84}
{"timestamp": "2025-12-31T17:42:56Z", "level": "INFO", "message": "Copied Behind the Scenes - The Digital Artistry of Pacific Rim.mkv → Behind the Scenes - The Digital Artistry of Pacific Rim.mkv", "module": "process_manager", "funcName": "process_folder", "line": 89}
{"timestamp": "2025-12-31T17:42:56Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T17:42:56Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T17:42:56Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 110}
{"timestamp": "2025-12-31T17:42:56Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T17:42:56Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T17:42:56Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T17:42:56Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T17:42:56Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T17:42:56Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T17:42:56Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T17:42:56Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T17:42:56Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T17:42:56Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T17:42:56Z", "level": "INFO", "message": "Running CQ encode: Behind the Scenes - The Digital Artistry of Pacific Rim -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T17:43:37Z", "level": "INFO", "message": "Using path as-is: P:\\movies\\Pacific Rim (2013)", "module": "main", "funcName": "normalize_input_path", "line": 49}
{"timestamp": "2025-12-31T17:43:37Z", "level": "INFO", "message": "Processing: Pacific Rim (2013) x265 AAC 7.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T17:44:25Z", "level": "INFO", "message": "Copied Pacific Rim (2013) x265 AAC 7.1 Bluray-1080p Tigole.mkv → Pacific Rim (2013) x265 AAC 7.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T17:44:25Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T17:44:25Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T17:44:25Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T17:44:32Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T17:44:32Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T17:44:32Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T17:44:32Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T17:44:32Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T17:44:32Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T17:44:32Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T17:44:32Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T17:44:32Z", "level": "INFO", "message": " Audio Streams (2 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T17:44:32Z", "level": "INFO", "message": " - Stream #1: 8ch→6ch | Lang: und | Detected: 617kbps | Action: ENCODE | Target: 448kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T17:44:32Z", "level": "INFO", "message": " - Stream #2: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T17:44:32Z", "level": "INFO", "message": "Running CQ encode: Pacific Rim (2013) x265 AAC 7.1 Bluray-1080p Tigole -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T18:01:19Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T18:01:19Z", "level": "INFO", "message": " Original Size: 5624.98 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T18:01:19Z", "level": "INFO", "message": " Encoded Size: 4029.53 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T18:01:19Z", "level": "INFO", "message": " Reduction: 71.6% of original (28.4% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T18:01:19Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T18:01:19Z", "level": "INFO", "message": " Audio Streams: 2 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T18:01:57Z", "level": "INFO", "message": "Moved Pacific Rim (2013) x265 AAC 7.1 Bluray-1080p Tigole -EHX.mkv → Pacific Rim (2013) x265 AAC 7.1 Bluray-1080p Tigole -EHX.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 346}
{"timestamp": "2025-12-31T18:02:04Z", "level": "INFO", "message": "\n✅ CONVERSION COMPLETE: Pacific Rim (2013) x265 AAC 7.1 Bluray-1080p Tigole -EHX.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 386}
{"timestamp": "2025-12-31T18:02:04Z", "level": "INFO", "message": " Type: MOVIE | Show: N/A", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 387}
{"timestamp": "2025-12-31T18:02:04Z", "level": "INFO", "message": " Size: 5624.98MB → 4029.53MB (71.6% of original, 28.4% reduction)", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 388}
{"timestamp": "2025-12-31T18:02:04Z", "level": "INFO", "message": " Method: CQ | Status: SUCCESS", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 389}
{"timestamp": "2025-12-31T18:02:05Z", "level": "INFO", "message": "Deleted original and processing copy for Pacific Rim (2013) x265 AAC 7.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 395}
{"timestamp": "2025-12-31T18:02:05Z", "level": "INFO", "message": "Processing: Behind the Scenes - Drift Space.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T18:02:06Z", "level": "INFO", "message": "Copied Behind the Scenes - Drift Space.mkv → Behind the Scenes - Drift Space.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T18:02:06Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T18:02:06Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T18:02:06Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T18:02:06Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T18:02:06Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T18:02:06Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T18:02:06Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T18:02:06Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T18:02:06Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T18:02:06Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T18:02:06Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T18:02:06Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T18:02:06Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T18:02:06Z", "level": "INFO", "message": "Running CQ encode: Behind the Scenes - Drift Space -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T18:02:27Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T18:02:27Z", "level": "INFO", "message": " Original Size: 64.77 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T18:02:27Z", "level": "INFO", "message": " Encoded Size: 79.36 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T18:02:27Z", "level": "INFO", "message": " Reduction: 122.5% of original (-22.5% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T18:02:27Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T18:02:27Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T18:02:27Z", "level": "INFO", "message": "Processing: Behind the Scenes - The Digital Artistry of Pacific Rim.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T18:02:29Z", "level": "INFO", "message": "Copied Behind the Scenes - The Digital Artistry of Pacific Rim.mkv → Behind the Scenes - The Digital Artistry of Pacific Rim.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T18:02:29Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T18:02:29Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T18:02:29Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T18:02:29Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T18:02:29Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T18:02:29Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T18:02:29Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T18:02:29Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T18:02:29Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T18:02:29Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T18:02:29Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T18:02:29Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T18:02:29Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T18:02:29Z", "level": "INFO", "message": "Running CQ encode: Behind the Scenes - The Digital Artistry of Pacific Rim -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T18:03:26Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T18:03:26Z", "level": "INFO", "message": " Original Size: 207.39 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T18:03:26Z", "level": "INFO", "message": " Encoded Size: 375.19 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T18:03:26Z", "level": "INFO", "message": " Reduction: 180.9% of original (-80.9% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T18:03:26Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T18:03:26Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T18:03:26Z", "level": "INFO", "message": "Processing: Behind the Scenes - The Shatterdome.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T18:03:27Z", "level": "INFO", "message": "Copied Behind the Scenes - The Shatterdome.mkv → Behind the Scenes - The Shatterdome.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T18:03:28Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T18:03:28Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T18:03:28Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T18:03:28Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T18:03:28Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T18:03:28Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T18:03:28Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T18:03:28Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T18:03:28Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T18:03:28Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T18:03:28Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T18:03:28Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T18:03:28Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T18:03:28Z", "level": "INFO", "message": "Running CQ encode: Behind the Scenes - The Shatterdome -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T18:04:07Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T18:04:07Z", "level": "INFO", "message": " Original Size: 116.78 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T18:04:07Z", "level": "INFO", "message": " Encoded Size: 203.90 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T18:04:07Z", "level": "INFO", "message": " Reduction: 174.6% of original (-74.6% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T18:04:07Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T18:04:07Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T18:04:07Z", "level": "ERROR", "message": "3 consecutive failures. Stopping process.", "module": "process_manager", "funcName": "process_folder", "line": 189}
{"timestamp": "2025-12-31T18:04:07Z", "level": "INFO", "message": "Batch processing complete", "module": "process_manager", "funcName": "process_folder", "line": 336}
{"timestamp": "2025-12-31T18:13:31Z", "level": "INFO", "message": "Using path as-is: P:\\movies\\Planes, Trains & Automobiles (1987)", "module": "main", "funcName": "normalize_input_path", "line": 49}
{"timestamp": "2025-12-31T18:13:31Z", "level": "INFO", "message": "Processing: Planes, Trains and Automobiles (1987) x265 AAC 5.1 Bluray-1080p afm72.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T18:14:10Z", "level": "INFO", "message": "Copied Planes, Trains and Automobiles (1987) x265 AAC 5.1 Bluray-1080p afm72.mkv → Planes, Trains and Automobiles (1987) x265 AAC 5.1 Bluray-1080p afm72.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T18:14:10Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T18:14:10Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T18:14:10Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T18:14:12Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T18:14:12Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T18:14:12Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T18:14:12Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T18:14:12Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T18:14:12Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T18:14:12Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T18:14:12Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T18:14:12Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T18:14:12Z", "level": "INFO", "message": " - Stream #1: 6ch→6ch | Lang: und | Detected: 445kbps | Action: ENCODE | Target: 384kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T18:14:12Z", "level": "INFO", "message": "Running CQ encode: Planes, Trains and Automobiles (1987) x265 AAC 5.1 Bluray-1080p afm72 -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T18:20:53Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T18:20:53Z", "level": "INFO", "message": " Original Size: 4489.88 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T18:20:53Z", "level": "INFO", "message": " Encoded Size: 2513.95 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T18:20:53Z", "level": "INFO", "message": " Reduction: 56.0% of original (44.0% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T18:20:53Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T18:20:53Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T18:21:15Z", "level": "INFO", "message": "Moved Planes, Trains and Automobiles (1987) x265 AAC 5.1 Bluray-1080p afm72 -EHX.mkv → Planes, Trains and Automobiles (1987) x265 AAC 5.1 Bluray-1080p afm72 -EHX.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 346}
{"timestamp": "2025-12-31T18:21:21Z", "level": "INFO", "message": "\n✅ CONVERSION COMPLETE: Planes, Trains and Automobiles (1987) x265 AAC 5.1 Bluray-1080p afm72 -EHX.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 386}
{"timestamp": "2025-12-31T18:21:21Z", "level": "INFO", "message": " Type: MOVIE | Show: N/A", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 387}
{"timestamp": "2025-12-31T18:21:21Z", "level": "INFO", "message": " Size: 4489.88MB → 2513.95MB (56.0% of original, 44.0% reduction)", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 388}
{"timestamp": "2025-12-31T18:21:21Z", "level": "INFO", "message": " Method: CQ | Status: SUCCESS", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 389}
{"timestamp": "2025-12-31T18:21:22Z", "level": "INFO", "message": "Deleted original and processing copy for Planes, Trains and Automobiles (1987) x265 AAC 5.1 Bluray-1080p afm72.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 395}
{"timestamp": "2025-12-31T18:21:22Z", "level": "INFO", "message": "Batch processing complete", "module": "process_manager", "funcName": "process_folder", "line": 336}
{"timestamp": "2025-12-31T18:21:22Z", "level": "INFO", "message": "Using path as-is: \\mnt\\plex\\movies\\Hackers (1995)", "module": "main", "funcName": "normalize_input_path", "line": 49}
{"timestamp": "2025-12-31T18:21:22Z", "level": "ERROR", "message": "Folder not found: \\mnt\\plex\\movies\\Hackers (1995)", "module": "main", "funcName": "main", "line": 109}
{"timestamp": "2025-12-31T18:21:22Z", "level": "INFO", "message": "Using path as-is: \\mnt\\plex\\movies\\Bullet Train (2022)", "module": "main", "funcName": "normalize_input_path", "line": 49}
{"timestamp": "2025-12-31T18:21:22Z", "level": "ERROR", "message": "Folder not found: \\mnt\\plex\\movies\\Bullet Train (2022)", "module": "main", "funcName": "main", "line": 109}
{"timestamp": "2025-12-31T18:21:23Z", "level": "INFO", "message": "Using path as-is: \\mnt\\plex\\movies\\The Truman Show (1998)", "module": "main", "funcName": "normalize_input_path", "line": 49}
{"timestamp": "2025-12-31T18:21:23Z", "level": "ERROR", "message": "Folder not found: \\mnt\\plex\\movies\\The Truman Show (1998)", "module": "main", "funcName": "main", "line": 109}
{"timestamp": "2025-12-31T18:25:45Z", "level": "INFO", "message": "Using path as-is: P:\\movies\\Planes, Trains & Automobiles (1987)", "module": "main", "funcName": "normalize_input_path", "line": 49}
{"timestamp": "2025-12-31T18:25:45Z", "level": "INFO", "message": "Skipping: Planes, Trains and Automobiles (1987) AV1 AAC 5.1 Bluray-1080p EHX.mkv", "module": "process_manager", "funcName": "process_folder", "line": 97}
{"timestamp": "2025-12-31T18:25:45Z", "level": "INFO", "message": "Batch processing complete", "module": "process_manager", "funcName": "process_folder", "line": 336}
{"timestamp": "2025-12-31T18:25:46Z", "level": "INFO", "message": "Path mapping: /mnt/plex/movies/Hackers (1995) -> P:\\movies\\Hackers (1995)", "module": "main", "funcName": "normalize_input_path", "line": 41}
{"timestamp": "2025-12-31T18:25:46Z", "level": "INFO", "message": "Processing: Hackers (1995) x265 AAC 5.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T18:26:34Z", "level": "INFO", "message": "Copied Hackers (1995) x265 AAC 5.1 Bluray-1080p Tigole.mkv → Hackers (1995) x265 AAC 5.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T18:26:34Z", "level": "INFO", "message": "Source resolution detected: 1920x804", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T18:26:34Z", "level": "INFO", "message": "Source 1920x804 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T18:26:34Z", "level": "INFO", "message": "Source 1920x804 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T18:26:40Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T18:26:40Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T18:26:40Z", "level": "INFO", "message": " • Source Resolution: 1920x804", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T18:26:40Z", "level": "INFO", "message": " • Target Resolution: 1920x804", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T18:26:40Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T18:26:40Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T18:26:40Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T18:26:40Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T18:26:40Z", "level": "INFO", "message": " Audio Streams (2 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T18:26:40Z", "level": "INFO", "message": " - Stream #1: 6ch→6ch | Lang: und | Detected: 511kbps | Action: ENCODE | Target: 448kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T18:26:40Z", "level": "INFO", "message": " - Stream #2: 2ch→2ch | Lang: und | Detected: 72kbps | Action: COPY (preserve) | Target: 72kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T18:26:40Z", "level": "INFO", "message": "Running CQ encode: Hackers (1995) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T18:37:33Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T18:37:33Z", "level": "INFO", "message": " Original Size: 5601.42 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T18:37:33Z", "level": "INFO", "message": " Encoded Size: 2346.49 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T18:37:33Z", "level": "INFO", "message": " Reduction: 41.9% of original (58.1% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T18:37:33Z", "level": "INFO", "message": " Resolution: 1920x804 → 1920x804", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T18:37:33Z", "level": "INFO", "message": " Audio Streams: 2 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T18:37:54Z", "level": "INFO", "message": "Moved Hackers (1995) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv → Hackers (1995) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 346}
{"timestamp": "2025-12-31T18:38:00Z", "level": "INFO", "message": "\n✅ CONVERSION COMPLETE: Hackers (1995) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 386}
{"timestamp": "2025-12-31T18:38:00Z", "level": "INFO", "message": " Type: MOVIE | Show: N/A", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 387}
{"timestamp": "2025-12-31T18:38:00Z", "level": "INFO", "message": " Size: 5601.42MB → 2346.49MB (41.9% of original, 58.1% reduction)", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 388}
{"timestamp": "2025-12-31T18:38:00Z", "level": "INFO", "message": " Method: CQ | Status: SUCCESS", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 389}
{"timestamp": "2025-12-31T18:38:01Z", "level": "INFO", "message": "Deleted original and processing copy for Hackers (1995) x265 AAC 5.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 395}
{"timestamp": "2025-12-31T18:38:01Z", "level": "INFO", "message": "Processing: The Keyboard Cowboys - A Look Back at Hackers.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T18:38:07Z", "level": "INFO", "message": "Copied The Keyboard Cowboys - A Look Back at Hackers.mkv → The Keyboard Cowboys - A Look Back at Hackers.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T18:38:07Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T18:38:07Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T18:38:07Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T18:38:08Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T18:38:08Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T18:38:08Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T18:38:08Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T18:38:08Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T18:38:08Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T18:38:08Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T18:38:08Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T18:38:08Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T18:38:08Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 67kbps | Action: COPY (preserve) | Target: 67kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T18:38:08Z", "level": "INFO", "message": "Running CQ encode: The Keyboard Cowboys - A Look Back at Hackers -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T18:40:45Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T18:40:45Z", "level": "INFO", "message": " Original Size: 641.76 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T18:40:45Z", "level": "INFO", "message": " Encoded Size: 866.95 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T18:40:45Z", "level": "INFO", "message": " Reduction: 135.1% of original (-35.1% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T18:40:45Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T18:40:45Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T18:40:45Z", "level": "INFO", "message": "Processing: Trailer.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T18:40:45Z", "level": "INFO", "message": "Copied Trailer.mkv → Trailer.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T18:40:45Z", "level": "INFO", "message": "Source resolution detected: 1920x848", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T18:40:45Z", "level": "INFO", "message": "Source 1920x848 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T18:40:45Z", "level": "INFO", "message": "Source 1920x848 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T18:40:46Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T18:40:46Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T18:40:46Z", "level": "INFO", "message": " • Source Resolution: 1920x848", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T18:40:46Z", "level": "INFO", "message": " • Target Resolution: 1920x848", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T18:40:46Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T18:40:46Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T18:40:46Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T18:40:46Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T18:40:46Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T18:40:46Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 67kbps | Action: COPY (preserve) | Target: 67kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T18:40:46Z", "level": "INFO", "message": "Running CQ encode: Trailer -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T18:40:54Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T18:40:54Z", "level": "INFO", "message": " Original Size: 39.28 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T18:40:54Z", "level": "INFO", "message": " Encoded Size: 48.99 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T18:40:54Z", "level": "INFO", "message": " Reduction: 124.7% of original (-24.7% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T18:40:54Z", "level": "INFO", "message": " Resolution: 1920x848 → 1920x848", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T18:40:54Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T18:40:54Z", "level": "INFO", "message": "Batch processing complete", "module": "process_manager", "funcName": "process_folder", "line": 336}
{"timestamp": "2025-12-31T18:40:55Z", "level": "INFO", "message": "Path mapping: /mnt/plex/movies/Bullet Train (2022) -> P:\\movies\\Bullet Train (2022)", "module": "main", "funcName": "normalize_input_path", "line": 41}
{"timestamp": "2025-12-31T18:40:55Z", "level": "INFO", "message": "Processing: Bullet Train (2022) x265 AAC 5.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T18:41:44Z", "level": "INFO", "message": "Copied Bullet Train (2022) x265 AAC 5.1 Bluray-1080p Tigole.mkv → Bullet Train (2022) x265 AAC 5.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T18:41:44Z", "level": "INFO", "message": "Source resolution detected: 1920x804", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T18:41:44Z", "level": "INFO", "message": "Source 1920x804 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T18:41:44Z", "level": "INFO", "message": "Source 1920x804 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T18:41:52Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T18:41:52Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T18:41:52Z", "level": "INFO", "message": " • Source Resolution: 1920x804", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T18:41:52Z", "level": "INFO", "message": " • Target Resolution: 1920x804", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T18:41:52Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T18:41:52Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T18:41:52Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T18:41:52Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T18:41:52Z", "level": "INFO", "message": " Audio Streams (2 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T18:41:52Z", "level": "INFO", "message": " - Stream #1: 6ch→6ch | Lang: und | Detected: 391kbps | Action: ENCODE | Target: 384kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T18:41:52Z", "level": "INFO", "message": " - Stream #2: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T18:41:52Z", "level": "INFO", "message": "Running CQ encode: Bullet Train (2022) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T18:50:44Z", "level": "INFO", "message": "Added to paths.txt: --r 720 --m bitrate \"P:\\movies\\The Lord of the Rings - The Fellowship of the Ring - Extended Edition (2001)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 317}
{"timestamp": "2025-12-31T18:50:45Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T18:50:45Z", "level": "INFO", "message": " Original Size: 5857.37 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T18:50:45Z", "level": "INFO", "message": " Encoded Size: 2444.68 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T18:50:45Z", "level": "INFO", "message": " Reduction: 41.7% of original (58.3% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T18:50:45Z", "level": "INFO", "message": " Resolution: 1920x804 → 1920x804", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T18:50:45Z", "level": "INFO", "message": " Audio Streams: 2 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T18:51:07Z", "level": "INFO", "message": "Moved Bullet Train (2022) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv → Bullet Train (2022) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 346}
{"timestamp": "2025-12-31T18:51:18Z", "level": "INFO", "message": "\n✅ CONVERSION COMPLETE: Bullet Train (2022) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 386}
{"timestamp": "2025-12-31T18:51:18Z", "level": "INFO", "message": " Type: MOVIE | Show: N/A", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 387}
{"timestamp": "2025-12-31T18:51:18Z", "level": "INFO", "message": " Size: 5857.37MB → 2444.68MB (41.7% of original, 58.3% reduction)", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 388}
{"timestamp": "2025-12-31T18:51:18Z", "level": "INFO", "message": " Method: CQ | Status: SUCCESS", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 389}
{"timestamp": "2025-12-31T18:51:19Z", "level": "INFO", "message": "Deleted original and processing copy for Bullet Train (2022) x265 AAC 5.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 395}
{"timestamp": "2025-12-31T18:51:19Z", "level": "INFO", "message": "Batch processing complete", "module": "process_manager", "funcName": "process_folder", "line": 336}
{"timestamp": "2025-12-31T18:51:19Z", "level": "INFO", "message": "Path mapping: /mnt/plex/movies/The Truman Show (1998) -> P:\\movies\\The Truman Show (1998)", "module": "main", "funcName": "normalize_input_path", "line": 41}
{"timestamp": "2025-12-31T18:51:19Z", "level": "INFO", "message": "Processing: The Truman Show (1998) x265 AAC 5.1 Bluray-1080p Silence.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T18:52:03Z", "level": "INFO", "message": "Copied The Truman Show (1998) x265 AAC 5.1 Bluray-1080p Silence.mkv → The Truman Show (1998) x265 AAC 5.1 Bluray-1080p Silence.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T18:52:03Z", "level": "INFO", "message": "Source resolution detected: 1918x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T18:52:03Z", "level": "INFO", "message": "Source 1918x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T18:52:03Z", "level": "INFO", "message": "Source 1918x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T18:52:10Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T18:52:10Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T18:52:10Z", "level": "INFO", "message": " • Source Resolution: 1918x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T18:52:10Z", "level": "INFO", "message": " • Target Resolution: 1918x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T18:52:10Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T18:52:10Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T18:52:10Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T18:52:10Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T18:52:10Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T18:52:10Z", "level": "INFO", "message": " - Stream #1: 6ch→6ch | Lang: und | Detected: 592kbps | Action: ENCODE | Target: 448kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T18:52:10Z", "level": "INFO", "message": "Running CQ encode: The Truman Show (1998) x265 AAC 5.1 Bluray-1080p Silence -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T18:54:22Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\The Lord of the Rings - The Return of the King - Extended Edition (2003)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 323}
{"timestamp": "2025-12-31T18:55:54Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\The Lord of the Rings - The Return of the King - Extended Edition (2003)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 323}
{"timestamp": "2025-12-31T19:01:20Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T19:01:20Z", "level": "INFO", "message": " Original Size: 5152.45 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T19:01:20Z", "level": "INFO", "message": " Encoded Size: 2971.12 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T19:01:20Z", "level": "INFO", "message": " Reduction: 57.7% of original (42.3% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T19:01:20Z", "level": "INFO", "message": " Resolution: 1918x1080 → 1918x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T19:01:20Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T19:01:47Z", "level": "INFO", "message": "Moved The Truman Show (1998) x265 AAC 5.1 Bluray-1080p Silence -EHX.mkv → The Truman Show (1998) x265 AAC 5.1 Bluray-1080p Silence -EHX.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 346}
{"timestamp": "2025-12-31T19:01:53Z", "level": "INFO", "message": "\n✅ CONVERSION COMPLETE: The Truman Show (1998) x265 AAC 5.1 Bluray-1080p Silence -EHX.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 386}
{"timestamp": "2025-12-31T19:01:53Z", "level": "INFO", "message": " Type: MOVIE | Show: N/A", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 387}
{"timestamp": "2025-12-31T19:01:53Z", "level": "INFO", "message": " Size: 5152.45MB → 2971.12MB (57.7% of original, 42.3% reduction)", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 388}
{"timestamp": "2025-12-31T19:01:53Z", "level": "INFO", "message": " Method: CQ | Status: SUCCESS", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 389}
{"timestamp": "2025-12-31T19:01:54Z", "level": "INFO", "message": "Deleted original and processing copy for The Truman Show (1998) x265 AAC 5.1 Bluray-1080p Silence.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 395}
{"timestamp": "2025-12-31T19:01:54Z", "level": "INFO", "message": "Processing: The Making of The Truman Show.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T19:01:56Z", "level": "INFO", "message": "Copied The Making of The Truman Show.mkv → The Making of The Truman Show.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T19:01:56Z", "level": "INFO", "message": "Source resolution detected: 720x480", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T19:01:56Z", "level": "INFO", "message": "Source 720x480 (<=720p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 65}
{"timestamp": "2025-12-31T19:01:56Z", "level": "INFO", "message": "Source 720x480 (<=720p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 124}
{"timestamp": "2025-12-31T19:01:58Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T19:01:58Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T19:01:58Z", "level": "INFO", "message": " • Source Resolution: 720x480", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T19:01:58Z", "level": "INFO", "message": " • Target Resolution: 720x480", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T19:01:58Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T19:01:58Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T19:01:58Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T19:01:58Z", "level": "INFO", "message": " • CQ Value: 34", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T19:01:58Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T19:01:58Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 81kbps | Action: COPY (preserve) | Target: 81kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T19:01:58Z", "level": "INFO", "message": "Running CQ encode: The Making of The Truman Show -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T19:02:32Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T19:02:32Z", "level": "INFO", "message": " Original Size: 172.27 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T19:02:32Z", "level": "INFO", "message": " Encoded Size: 202.00 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T19:02:32Z", "level": "INFO", "message": " Reduction: 117.3% of original (-17.3% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T19:02:32Z", "level": "INFO", "message": " Resolution: 720x480 → 720x480", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T19:02:32Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T19:02:32Z", "level": "INFO", "message": "Processing: The Visual Effects of The Truman Show.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T19:02:33Z", "level": "INFO", "message": "Copied The Visual Effects of The Truman Show.mkv → The Visual Effects of The Truman Show.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T19:02:33Z", "level": "INFO", "message": "Source resolution detected: 720x480", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T19:02:33Z", "level": "INFO", "message": "Source 720x480 (<=720p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 65}
{"timestamp": "2025-12-31T19:02:33Z", "level": "INFO", "message": "Source 720x480 (<=720p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 124}
{"timestamp": "2025-12-31T19:02:33Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T19:02:33Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T19:02:33Z", "level": "INFO", "message": " • Source Resolution: 720x480", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T19:02:33Z", "level": "INFO", "message": " • Target Resolution: 720x480", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T19:02:33Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T19:02:33Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T19:02:33Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T19:02:33Z", "level": "INFO", "message": " • CQ Value: 34", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T19:02:33Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T19:02:33Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 81kbps | Action: COPY (preserve) | Target: 81kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T19:02:33Z", "level": "INFO", "message": "Running CQ encode: The Visual Effects of The Truman Show -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T19:02:45Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T19:02:45Z", "level": "INFO", "message": " Original Size: 63.59 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T19:02:45Z", "level": "INFO", "message": " Encoded Size: 67.60 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T19:02:45Z", "level": "INFO", "message": " Reduction: 106.3% of original (-6.3% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T19:02:45Z", "level": "INFO", "message": " Resolution: 720x480 → 720x480", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T19:02:45Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T19:02:45Z", "level": "INFO", "message": "Processing: Product Placement.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T19:02:46Z", "level": "INFO", "message": "Copied Product Placement.mkv → Product Placement.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T19:02:46Z", "level": "INFO", "message": "Source resolution detected: 720x480", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T19:02:46Z", "level": "INFO", "message": "Source 720x480 (<=720p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 65}
{"timestamp": "2025-12-31T19:02:46Z", "level": "INFO", "message": "Source 720x480 (<=720p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 124}
{"timestamp": "2025-12-31T19:02:46Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T19:02:46Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T19:02:46Z", "level": "INFO", "message": " • Source Resolution: 720x480", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T19:02:46Z", "level": "INFO", "message": " • Target Resolution: 720x480", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T19:02:46Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T19:02:46Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T19:02:46Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T19:02:46Z", "level": "INFO", "message": " • CQ Value: 34", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T19:02:46Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T19:02:46Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 81kbps | Action: COPY (preserve) | Target: 81kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T19:02:46Z", "level": "INFO", "message": "Running CQ encode: Product Placement -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T19:02:51Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T19:02:51Z", "level": "INFO", "message": " Original Size: 26.56 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T19:02:51Z", "level": "INFO", "message": " Encoded Size: 28.37 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T19:02:51Z", "level": "INFO", "message": " Reduction: 106.8% of original (-6.8% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T19:02:51Z", "level": "INFO", "message": " Resolution: 720x480 → 720x480", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T19:02:51Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T19:02:51Z", "level": "ERROR", "message": "3 consecutive failures. Stopping process.", "module": "process_manager", "funcName": "process_folder", "line": 189}
{"timestamp": "2025-12-31T19:02:51Z", "level": "INFO", "message": "Batch processing complete", "module": "process_manager", "funcName": "process_folder", "line": 336}
{"timestamp": "2025-12-31T19:05:59Z", "level": "INFO", "message": "paths.txt cleared", "module": "gui_path_manager", "funcName": "_clear_paths_file", "line": 363}
{"timestamp": "2025-12-31T19:08:45Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\John Wick - Chapter 4 (2023)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 323}
{"timestamp": "2025-12-31T19:08:55Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\The Lord of the Rings - The Return of the King - Extended Edition (2003)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 323}
{"timestamp": "2025-12-31T19:08:58Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\F1 (2025)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 323}
{"timestamp": "2025-12-31T19:09:00Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\John Wick - Chapter 3 - Parabellum (2019)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 323}
{"timestamp": "2025-12-31T19:09:03Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\Starship Troopers (1997)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 323}
{"timestamp": "2025-12-31T19:09:31Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\Ferris Bueller's Day Off (1986)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 323}
{"timestamp": "2025-12-31T19:09:47Z", "level": "INFO", "message": "Using path as-is: P:\\movies\\John Wick - Chapter 4 (2023)", "module": "main", "funcName": "normalize_input_path", "line": 49}
{"timestamp": "2025-12-31T19:09:47Z", "level": "INFO", "message": "Processing: John Wick - Chapter 4 (2023) x265 AAC 7.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T19:10:57Z", "level": "INFO", "message": "Copied John Wick - Chapter 4 (2023) x265 AAC 7.1 Bluray-1080p Tigole.mkv → John Wick - Chapter 4 (2023) x265 AAC 7.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T19:10:57Z", "level": "INFO", "message": "Source resolution detected: 1920x804", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T19:10:57Z", "level": "INFO", "message": "Source 1920x804 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T19:10:57Z", "level": "INFO", "message": "Source 1920x804 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T19:11:09Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T19:11:09Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T19:11:09Z", "level": "INFO", "message": " • Source Resolution: 1920x804", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T19:11:09Z", "level": "INFO", "message": " • Target Resolution: 1920x804", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T19:11:09Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T19:11:09Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T19:11:09Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T19:11:09Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T19:11:09Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T19:11:09Z", "level": "INFO", "message": " - Stream #1: 8ch→6ch | Lang: und | Detected: 599kbps | Action: ENCODE | Target: 448kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T19:11:09Z", "level": "INFO", "message": "Running CQ encode: John Wick - Chapter 4 (2023) x265 AAC 7.1 Bluray-1080p Tigole -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T19:18:49Z", "level": "INFO", "message": "Added to paths.txt: \"P:\\movies\\Furious 7 (2015)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 452}
{"timestamp": "2025-12-31T19:19:12Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\Furious 7 (2015)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 452}
{"timestamp": "2025-12-31T19:19:36Z", "level": "INFO", "message": "Added to paths.txt: \"P:\\movies\\Pirates of the Caribbean - The Curse of the Black Pearl (2003)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 452}
{"timestamp": "2025-12-31T19:19:42Z", "level": "INFO", "message": "Added to paths.txt: \"P:\\movies\\Fast & Furious 6 (2013)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 452}
{"timestamp": "2025-12-31T19:21:19Z", "level": "INFO", "message": "Removed from paths.txt: P:\\movies\\Pirates of the Caribbean - The Curse of the Black Pearl (2003)", "module": "gui_path_manager", "funcName": "_remove_from_paths_file", "line": 504}
{"timestamp": "2025-12-31T19:21:37Z", "level": "INFO", "message": "Removed from paths.txt: P:\\movies\\Fast & Furious 6 (2013)", "module": "gui_path_manager", "funcName": "_remove_from_paths_file", "line": 504}
{"timestamp": "2025-12-31T19:21:51Z", "level": "INFO", "message": "Removed from paths.txt: P:\\movies\\Furious 7 (2015)", "module": "gui_path_manager", "funcName": "_remove_from_paths_file", "line": 504}
{"timestamp": "2025-12-31T19:21:58Z", "level": "INFO", "message": "Removed from paths.txt: P:\\movies\\Ferris Bueller's Day Off (1986)", "module": "gui_path_manager", "funcName": "_remove_from_paths_file", "line": 504}
{"timestamp": "2025-12-31T19:22:07Z", "level": "INFO", "message": "Added to paths.txt: \"P:\\movies\\Ferris Bueller's Day Off (1986)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 461}
{"timestamp": "2025-12-31T19:23:37Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T19:23:37Z", "level": "INFO", "message": " Original Size: 8144.41 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T19:23:37Z", "level": "INFO", "message": " Encoded Size: 3288.02 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T19:23:37Z", "level": "INFO", "message": " Reduction: 40.4% of original (59.6% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T19:23:37Z", "level": "INFO", "message": " Resolution: 1920x804 → 1920x804", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T19:23:37Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T19:24:07Z", "level": "INFO", "message": "Moved John Wick - Chapter 4 (2023) x265 AAC 7.1 Bluray-1080p Tigole -EHX.mkv → John Wick - Chapter 4 (2023) x265 AAC 7.1 Bluray-1080p Tigole -EHX.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 346}
{"timestamp": "2025-12-31T19:24:16Z", "level": "INFO", "message": "\n✅ CONVERSION COMPLETE: John Wick - Chapter 4 (2023) x265 AAC 7.1 Bluray-1080p Tigole -EHX.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 386}
{"timestamp": "2025-12-31T19:24:16Z", "level": "INFO", "message": " Type: MOVIE | Show: N/A", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 387}
{"timestamp": "2025-12-31T19:24:16Z", "level": "INFO", "message": " Size: 8144.41MB → 3288.02MB (40.4% of original, 59.6% reduction)", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 388}
{"timestamp": "2025-12-31T19:24:16Z", "level": "INFO", "message": " Method: CQ | Status: SUCCESS", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 389}
{"timestamp": "2025-12-31T19:24:17Z", "level": "INFO", "message": "Deleted original and processing copy for John Wick - Chapter 4 (2023) x265 AAC 7.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 395}
{"timestamp": "2025-12-31T19:24:17Z", "level": "INFO", "message": "Processing: A Shot in the Dark.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T19:24:18Z", "level": "INFO", "message": "Copied A Shot in the Dark.mkv → A Shot in the Dark.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T19:24:18Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T19:24:18Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T19:24:18Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T19:24:18Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T19:24:18Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T19:24:18Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T19:24:18Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T19:24:18Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T19:24:18Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T19:24:18Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T19:24:18Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T19:24:18Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T19:24:18Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T19:24:18Z", "level": "INFO", "message": "Running CQ encode: A Shot in the Dark -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T19:24:35Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T19:24:35Z", "level": "INFO", "message": " Original Size: 120.37 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T19:24:35Z", "level": "INFO", "message": " Encoded Size: 132.51 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T19:24:35Z", "level": "INFO", "message": " Reduction: 110.1% of original (-10.1% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T19:24:35Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T19:24:35Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T19:24:35Z", "level": "INFO", "message": "Processing: Chad and Keanu - Through Wick and Thin.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T19:24:36Z", "level": "INFO", "message": "Copied Chad and Keanu - Through Wick and Thin.mkv → Chad and Keanu - Through Wick and Thin.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T19:24:36Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T19:24:36Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T19:24:36Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T19:24:37Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T19:24:37Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T19:24:37Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T19:24:37Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T19:24:37Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T19:24:37Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T19:24:37Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T19:24:37Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T19:24:37Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T19:24:37Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T19:24:37Z", "level": "INFO", "message": "Running CQ encode: Chad and Keanu - Through Wick and Thin -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T19:24:52Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T19:24:52Z", "level": "INFO", "message": " Original Size: 94.32 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T19:24:52Z", "level": "INFO", "message": " Encoded Size: 113.04 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T19:24:52Z", "level": "INFO", "message": " Reduction: 119.8% of original (-19.8% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T19:24:52Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T19:24:52Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T19:24:52Z", "level": "INFO", "message": "Processing: In Honor of the Dead.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T19:24:53Z", "level": "INFO", "message": "Copied In Honor of the Dead.mkv → In Honor of the Dead.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T19:24:53Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T19:24:53Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T19:24:53Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T19:24:53Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T19:24:53Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T19:24:53Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T19:24:53Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T19:24:53Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T19:24:53Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T19:24:53Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T19:24:53Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T19:24:53Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T19:24:53Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T19:24:53Z", "level": "INFO", "message": "Running CQ encode: In Honor of the Dead -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T19:25:02Z", "level": "INFO", "message": "Added to paths.txt: \"P:\\movies\\The Lord of the Rings - The Two Towers - Extended Edition (2002)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 474}
{"timestamp": "2025-12-31T19:25:08Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T19:25:08Z", "level": "INFO", "message": " Original Size: 88.34 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T19:25:08Z", "level": "INFO", "message": " Encoded Size: 109.24 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T19:25:08Z", "level": "INFO", "message": " Reduction: 123.7% of original (-23.7% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T19:25:08Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T19:25:08Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T19:25:08Z", "level": "ERROR", "message": "3 consecutive failures. Stopping process.", "module": "process_manager", "funcName": "process_folder", "line": 189}
{"timestamp": "2025-12-31T19:25:08Z", "level": "INFO", "message": "Batch processing complete", "module": "process_manager", "funcName": "process_folder", "line": 336}
{"timestamp": "2025-12-31T19:25:09Z", "level": "INFO", "message": "Using path as-is: P:\\movies\\The Lord of the Rings - The Return of the King - Extended Edition (2003)", "module": "main", "funcName": "normalize_input_path", "line": 49}
{"timestamp": "2025-12-31T19:25:09Z", "level": "INFO", "message": "Processing: The Lord of the Rings - The Return of the King (2003) x265 EAC3 5.1 Bluray-1080p GalaxyRG265.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": "Copied The Lord of the Rings - The Return of the King (2003) x265 EAC3 5.1 Bluray-1080p GalaxyRG265.mkv → The Lord of the Rings - The Return of the King (2003) x265 EAC3 5.1 Bluray-1080p GalaxyRG265.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T19:26:19Z", "level": "WARNING", "message": "Failed to calculate bitrate for stream 0: Command '['ffmpeg', '-y', '-i', 'processing\\\\The Lord of the Rings - The Return of the King (2003) x265 EAC3 5.1 Bluray-1080p GalaxyRG265.mkv', '-map', '0:a:0', '-c', 'copy', 'C:\\\\Users\\\\Tyler\\\\AppData\\\\Local\\\\Temp\\\\tmpswtq225p.aac']' returned non-zero exit status 4294967274.. Will fall back to metadata.", "module": "audio_handler", "funcName": "calculate_stream_bitrate", "line": 74}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": "Stream 1: Using fallback bitrate 384 kbps", "module": "audio_handler", "funcName": "get_audio_streams", "line": 113}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": " - Stream #1: 6ch→6ch | Lang: und | Detected: 384kbps | Action: ENCODE | Target: 384kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T19:26:19Z", "level": "INFO", "message": "Running CQ encode: The Lord of the Rings - The Return of the King (2003) x265 EAC3 5.1 Bluray-1080p GalaxyRG265 -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T19:29:10Z", "level": "INFO", "message": "Loaded cache with 3195 entries", "module": "gui_path_manager", "funcName": "_load_cache", "line": 194}
{"timestamp": "2025-12-31T19:34:09Z", "level": "INFO", "message": "Loaded tv cache with 320 entries", "module": "gui_path_manager", "funcName": "_load_cache", "line": 205}
{"timestamp": "2025-12-31T19:34:12Z", "level": "INFO", "message": "Loaded movies cache with 2875 entries", "module": "gui_path_manager", "funcName": "_load_cache", "line": 205}
{"timestamp": "2025-12-31T19:35:18Z", "level": "INFO", "message": "Loaded tv cache with 320 entries", "module": "gui_path_manager", "funcName": "_load_cache", "line": 210}
{"timestamp": "2025-12-31T19:35:25Z", "level": "INFO", "message": "Loaded movies cache with 2875 entries", "module": "gui_path_manager", "funcName": "_load_cache", "line": 210}
{"timestamp": "2025-12-31T19:35:58Z", "level": "INFO", "message": "Loaded tv cache with 320 entries", "module": "gui_path_manager", "funcName": "_load_cache", "line": 210}
{"timestamp": "2025-12-31T19:36:09Z", "level": "INFO", "message": "Loaded movies cache with 2875 entries", "module": "gui_path_manager", "funcName": "_load_cache", "line": 210}
{"timestamp": "2025-12-31T19:37:07Z", "level": "INFO", "message": "Loaded tv cache with 320 entries", "module": "gui_path_manager", "funcName": "_load_cache", "line": 210}
{"timestamp": "2025-12-31T19:37:09Z", "level": "INFO", "message": "Loaded anime cache with 404 entries", "module": "gui_path_manager", "funcName": "_load_cache", "line": 210}
{"timestamp": "2025-12-31T19:37:13Z", "level": "INFO", "message": "Loaded tv cache with 320 entries", "module": "gui_path_manager", "funcName": "_load_cache", "line": 210}
{"timestamp": "2025-12-31T19:37:19Z", "level": "INFO", "message": "Cache file exists for tv", "module": "gui_path_manager", "funcName": "_load_cache", "line": 210}
{"timestamp": "2025-12-31T19:37:26Z", "level": "INFO", "message": "Cache file exists for movies", "module": "gui_path_manager", "funcName": "_load_cache", "line": 210}
{"timestamp": "2025-12-31T19:37:41Z", "level": "INFO", "message": "Cache file exists for tv", "module": "gui_path_manager", "funcName": "_load_cache", "line": 210}
{"timestamp": "2025-12-31T19:37:43Z", "level": "INFO", "message": "Cache file exists for movies", "module": "gui_path_manager", "funcName": "_load_cache", "line": 210}
{"timestamp": "2025-12-31T19:44:59Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T19:44:59Z", "level": "INFO", "message": " Original Size: 8033.94 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T19:44:59Z", "level": "INFO", "message": " Encoded Size: 6350.02 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T19:44:59Z", "level": "INFO", "message": " Reduction: 79.0% of original (21.0% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T19:44:59Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T19:44:59Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T19:45:01Z", "level": "INFO", "message": "Batch processing complete", "module": "process_manager", "funcName": "process_folder", "line": 336}
{"timestamp": "2025-12-31T19:45:01Z", "level": "INFO", "message": "Using path as-is: P:\\movies\\F1 (2025)", "module": "main", "funcName": "normalize_input_path", "line": 49}
{"timestamp": "2025-12-31T19:45:01Z", "level": "INFO", "message": "Processing: F1 (2025) x265 EAC3 7.1 Bluray-1080p SAMPA.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": "Copied F1 (2025) x265 EAC3 7.1 Bluray-1080p SAMPA.mkv → F1 (2025) x265 EAC3 7.1 Bluray-1080p SAMPA.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T19:46:09Z", "level": "WARNING", "message": "Failed to calculate bitrate for stream 0: Command '['ffmpeg', '-y', '-i', 'processing\\\\F1 (2025) x265 EAC3 7.1 Bluray-1080p SAMPA.mkv', '-map', '0:a:0', '-c', 'copy', 'C:\\\\Users\\\\Tyler\\\\AppData\\\\Local\\\\Temp\\\\tmp7_i4l73e.aac']' returned non-zero exit status 4294967274.. Will fall back to metadata.", "module": "audio_handler", "funcName": "calculate_stream_bitrate", "line": 74}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": "Stream 1: Using fallback bitrate 1024 kbps", "module": "audio_handler", "funcName": "get_audio_streams", "line": 113}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": " - Stream #1: 8ch→6ch | Lang: und | Detected: 1024kbps | Action: ENCODE | Target: 448kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T19:46:09Z", "level": "INFO", "message": "Running CQ encode: F1 (2025) x265 EAC3 7.1 Bluray-1080p SAMPA -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T19:58:31Z", "level": "INFO", "message": "Removed from paths.txt: P:\\movies\\Ferris Bueller's Day Off (1986)", "module": "gui_path_manager", "funcName": "_remove_from_paths_file", "line": 703}
{"timestamp": "2025-12-31T19:58:32Z", "level": "INFO", "message": "Removed from paths.txt: P:\\movies\\The Lord of the Rings - The Two Towers - Extended Edition (2002)", "module": "gui_path_manager", "funcName": "_remove_from_paths_file", "line": 703}
{"timestamp": "2025-12-31T20:04:57Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T20:04:57Z", "level": "INFO", "message": " Original Size: 7923.16 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T20:04:57Z", "level": "INFO", "message": " Encoded Size: 4044.90 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T20:04:57Z", "level": "INFO", "message": " Reduction: 51.1% of original (48.9% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T20:04:57Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T20:04:57Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T20:05:19Z", "level": "INFO", "message": "Removed from paths.txt: P:\\movies\\John Wick - Chapter 3 - Parabellum (2019)", "module": "gui_path_manager", "funcName": "_remove_from_paths_file", "line": 703}
{"timestamp": "2025-12-31T20:05:27Z", "level": "INFO", "message": "Added to paths.txt: \"P:\\movies\\John Wick - Chapter 3 - Parabellum (2019)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 660}
{"timestamp": "2025-12-31T20:05:33Z", "level": "INFO", "message": "Removed from paths.txt: P:\\movies\\F1 (2025)", "module": "gui_path_manager", "funcName": "_remove_from_paths_file", "line": 703}
{"timestamp": "2025-12-31T20:05:35Z", "level": "INFO", "message": "Moved F1 (2025) x265 EAC3 7.1 Bluray-1080p SAMPA -EHX.mkv → F1 (2025) x265 EAC3 7.1 Bluray-1080p SAMPA -EHX.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 346}
{"timestamp": "2025-12-31T20:05:35Z", "level": "WARNING", "message": "Failed to calculate bitrate for stream 0: Command '['ffmpeg', '-y', '-i', 'processing\\\\F1 (2025) x265 EAC3 7.1 Bluray-1080p SAMPA.mkv', '-map', '0:a:0', '-c', 'copy', 'C:\\\\Users\\\\Tyler\\\\AppData\\\\Local\\\\Temp\\\\tmpnjrbzkz0.aac']' returned non-zero exit status 4294967274.. Will fall back to metadata.", "module": "audio_handler", "funcName": "calculate_stream_bitrate", "line": 74}
{"timestamp": "2025-12-31T20:05:35Z", "level": "INFO", "message": "Stream 1: Using fallback bitrate 1024 kbps", "module": "audio_handler", "funcName": "get_audio_streams", "line": 113}
{"timestamp": "2025-12-31T20:05:35Z", "level": "INFO", "message": "\n✅ CONVERSION COMPLETE: F1 (2025) x265 EAC3 7.1 Bluray-1080p SAMPA -EHX.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 386}
{"timestamp": "2025-12-31T20:05:35Z", "level": "INFO", "message": " Type: MOVIE | Show: N/A", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 387}
{"timestamp": "2025-12-31T20:05:35Z", "level": "INFO", "message": " Size: 7923.16MB → 4044.9MB (51.1% of original, 48.9% reduction)", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 388}
{"timestamp": "2025-12-31T20:05:35Z", "level": "INFO", "message": " Method: CQ | Status: SUCCESS", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 389}
{"timestamp": "2025-12-31T20:05:37Z", "level": "INFO", "message": "Deleted original and processing copy for F1 (2025) x265 EAC3 7.1 Bluray-1080p SAMPA.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 395}
{"timestamp": "2025-12-31T20:05:37Z", "level": "INFO", "message": "Batch processing complete", "module": "process_manager", "funcName": "process_folder", "line": 336}
{"timestamp": "2025-12-31T20:05:37Z", "level": "INFO", "message": "Using path as-is: P:\\movies\\John Wick - Chapter 3 - Parabellum (2019)", "module": "main", "funcName": "normalize_input_path", "line": 49}
{"timestamp": "2025-12-31T20:05:37Z", "level": "INFO", "message": "Processing: John Wick - Chapter 3 - Parabellum (2019) x265 AAC 7.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T20:05:37Z", "level": "INFO", "message": "Removed from paths.txt: P:\\movies\\The Lord of the Rings - The Return of the King - Extended Edition (2003)", "module": "gui_path_manager", "funcName": "_remove_from_paths_file", "line": 703}
{"timestamp": "2025-12-31T20:05:53Z", "level": "INFO", "message": "Removed from paths.txt: P:\\movies\\John Wick - Chapter 3 - Parabellum (2019)", "module": "gui_path_manager", "funcName": "_remove_from_paths_file", "line": 703}
{"timestamp": "2025-12-31T20:05:55Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\John Wick - Chapter 3 - Parabellum (2019)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 660}
{"timestamp": "2025-12-31T20:06:15Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\John Wick - Chapter 2 (2017)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 660}
{"timestamp": "2025-12-31T20:06:20Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\Belle (2021)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 660}
{"timestamp": "2025-12-31T20:06:22Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\TAYLOR SWIFT THE ERAS TOUR (2023)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 660}
{"timestamp": "2025-12-31T20:06:25Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\Ferris Bueller's Day Off (1986)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 660}
{"timestamp": "2025-12-31T20:06:40Z", "level": "INFO", "message": "Added to paths.txt: --m cq --r 720 \"P:\\movies\\The Baker (2023)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 660}
{"timestamp": "2025-12-31T20:07:10Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\The Losers (2010)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 660}
{"timestamp": "2025-12-31T20:07:17Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\Violent Night (2022)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 660}
{"timestamp": "2025-12-31T20:07:27Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\Scott Pilgrim vs. the World (2010)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 660}
{"timestamp": "2025-12-31T20:07:36Z", "level": "INFO", "message": "Added to paths.txt: --m cq \"P:\\movies\\Small Soldiers (1998)\"", "module": "gui_path_manager", "funcName": "_add_to_paths_file", "line": 660}
{"timestamp": "2025-12-31T20:07:44Z", "level": "INFO", "message": "Launched transcode.bat", "module": "gui_path_manager", "funcName": "_run_transcode", "line": 737}
{"timestamp": "2025-12-31T20:08:38Z", "level": "INFO", "message": "Using path as-is: P:\\movies\\John Wick - Chapter 4 (2023)", "module": "main", "funcName": "normalize_input_path", "line": 49}
{"timestamp": "2025-12-31T20:08:38Z", "level": "INFO", "message": "Skipping: John Wick - Chapter 4 (2023) x265 AAC 7.1 Bluray-1080p Tigole -EHX.mkv", "module": "process_manager", "funcName": "process_folder", "line": 97}
{"timestamp": "2025-12-31T20:08:38Z", "level": "INFO", "message": "Processing: A Shot in the Dark.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T20:08:39Z", "level": "INFO", "message": "Copied A Shot in the Dark.mkv → A Shot in the Dark.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T20:08:39Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T20:08:39Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T20:08:39Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T20:08:40Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T20:08:40Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T20:08:40Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T20:08:40Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T20:08:40Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T20:08:40Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T20:08:40Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T20:08:40Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T20:08:40Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T20:08:40Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T20:08:40Z", "level": "INFO", "message": "Running CQ encode: A Shot in the Dark -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T20:09:01Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T20:09:01Z", "level": "INFO", "message": " Original Size: 120.37 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T20:09:01Z", "level": "INFO", "message": " Encoded Size: 132.51 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T20:09:01Z", "level": "INFO", "message": " Reduction: 110.1% of original (-10.1% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T20:09:01Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T20:09:01Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T20:09:01Z", "level": "INFO", "message": "Processing: Chad and Keanu - Through Wick and Thin.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T20:09:02Z", "level": "INFO", "message": "Copied Chad and Keanu - Through Wick and Thin.mkv → Chad and Keanu - Through Wick and Thin.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T20:09:02Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T20:09:02Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T20:09:02Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T20:09:02Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T20:09:02Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T20:09:02Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T20:09:02Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T20:09:02Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T20:09:02Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T20:09:02Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T20:09:02Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T20:09:02Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T20:09:02Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T20:09:02Z", "level": "INFO", "message": "Running CQ encode: Chad and Keanu - Through Wick and Thin -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T20:09:19Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T20:09:19Z", "level": "INFO", "message": " Original Size: 94.32 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T20:09:19Z", "level": "INFO", "message": " Encoded Size: 113.04 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T20:09:19Z", "level": "INFO", "message": " Reduction: 119.8% of original (-19.8% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T20:09:19Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T20:09:19Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T20:09:19Z", "level": "INFO", "message": "Processing: In Honor of the Dead.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T20:09:20Z", "level": "INFO", "message": "Copied In Honor of the Dead.mkv → In Honor of the Dead.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T20:09:20Z", "level": "INFO", "message": "Source resolution detected: 1920x1080", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T20:09:20Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T20:09:20Z", "level": "INFO", "message": "Source 1920x1080 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T20:09:20Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T20:09:20Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T20:09:20Z", "level": "INFO", "message": " • Source Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T20:09:20Z", "level": "INFO", "message": " • Target Resolution: 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T20:09:20Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T20:09:20Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T20:09:20Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T20:09:20Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T20:09:20Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T20:09:20Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T20:09:20Z", "level": "INFO", "message": "Running CQ encode: In Honor of the Dead -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T20:09:41Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T20:09:41Z", "level": "INFO", "message": " Original Size: 88.34 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T20:09:41Z", "level": "INFO", "message": " Encoded Size: 109.24 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T20:09:41Z", "level": "INFO", "message": " Reduction: 123.7% of original (-23.7% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T20:09:41Z", "level": "INFO", "message": " Resolution: 1920x1080 → 1920x1080", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T20:09:41Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T20:09:41Z", "level": "ERROR", "message": "3 consecutive failures. Stopping process.", "module": "process_manager", "funcName": "process_folder", "line": 189}
{"timestamp": "2025-12-31T20:09:41Z", "level": "INFO", "message": "Batch processing complete", "module": "process_manager", "funcName": "process_folder", "line": 336}
{"timestamp": "2025-12-31T20:09:41Z", "level": "INFO", "message": "Using path as-is: P:\\movies\\Starship Troopers (1997)", "module": "main", "funcName": "normalize_input_path", "line": 49}
{"timestamp": "2025-12-31T20:09:41Z", "level": "INFO", "message": "Processing: Starship Troopers (1997) x265 AAC 5.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T20:10:38Z", "level": "INFO", "message": "Copied Starship Troopers (1997) x265 AAC 5.1 Bluray-1080p Tigole.mkv → Starship Troopers (1997) x265 AAC 5.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T20:10:38Z", "level": "INFO", "message": "Source resolution detected: 1920x1040", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T20:10:38Z", "level": "INFO", "message": "Source 1920x1040 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T20:10:38Z", "level": "INFO", "message": "Source 1920x1040 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T20:10:52Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T20:10:52Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T20:10:52Z", "level": "INFO", "message": " • Source Resolution: 1920x1040", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T20:10:52Z", "level": "INFO", "message": " • Target Resolution: 1920x1040", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T20:10:52Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T20:10:52Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T20:10:52Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T20:10:52Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T20:10:52Z", "level": "INFO", "message": " Audio Streams (4 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T20:10:52Z", "level": "INFO", "message": " - Stream #1: 6ch→6ch | Lang: und | Detected: 485kbps | Action: ENCODE | Target: 448kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T20:10:52Z", "level": "INFO", "message": " - Stream #2: 2ch→2ch | Lang: und | Detected: 67kbps | Action: COPY (preserve) | Target: 67kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T20:10:52Z", "level": "INFO", "message": " - Stream #3: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T20:10:52Z", "level": "INFO", "message": " - Stream #4: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T20:10:52Z", "level": "INFO", "message": "Running CQ encode: Starship Troopers (1997) x265 AAC 5.1 Bluray-1080p Tigole -EHX.mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T20:14:37Z", "level": "INFO", "message": "Using path as-is: P:\\movies\\Starship Troopers (1997)", "module": "main", "funcName": "normalize_input_path", "line": 49}
{"timestamp": "2025-12-31T20:14:37Z", "level": "INFO", "message": "Processing: Starship Troopers (1997) x265 AAC 5.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T20:15:32Z", "level": "INFO", "message": "Copied Starship Troopers (1997) x265 AAC 5.1 Bluray-1080p Tigole.mkv → Starship Troopers (1997) x265 AAC 5.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T20:15:33Z", "level": "INFO", "message": "Source resolution detected: 1920x1040", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T20:15:33Z", "level": "INFO", "message": "Source 1920x1040 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T20:15:33Z", "level": "INFO", "message": "Source 1920x1040 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T20:15:46Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T20:15:46Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T20:15:46Z", "level": "INFO", "message": " • Source Resolution: 1920x1040", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T20:15:46Z", "level": "INFO", "message": " • Target Resolution: 1920x1040", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T20:15:46Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T20:15:46Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T20:15:46Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T20:15:46Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T20:15:46Z", "level": "INFO", "message": " Audio Streams (4 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T20:15:46Z", "level": "INFO", "message": " - Stream #1: 6ch→6ch | Lang: und | Detected: 486kbps | Action: ENCODE | Target: 448kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T20:15:46Z", "level": "INFO", "message": " - Stream #2: 2ch→2ch | Lang: und | Detected: 66kbps | Action: COPY (preserve) | Target: 66kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T20:15:46Z", "level": "INFO", "message": " - Stream #3: 2ch→2ch | Lang: und | Detected: 67kbps | Action: COPY (preserve) | Target: 67kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T20:15:46Z", "level": "INFO", "message": " - Stream #4: 2ch→2ch | Lang: und | Detected: 67kbps | Action: COPY (preserve) | Target: 67kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T20:15:46Z", "level": "INFO", "message": "Running CQ encode: Starship Troopers (1997) x265 AAC 5.1 Bluray-1080p Tigole - [EHX].mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T20:29:30Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T20:29:30Z", "level": "INFO", "message": " Original Size: 6522.97 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T20:29:30Z", "level": "INFO", "message": " Encoded Size: 4495.60 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T20:29:30Z", "level": "INFO", "message": " Reduction: 68.9% of original (31.1% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T20:29:30Z", "level": "INFO", "message": " Resolution: 1920x1040 → 1920x1040", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T20:29:30Z", "level": "INFO", "message": " Audio Streams: 4 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T20:30:11Z", "level": "INFO", "message": "Moved Starship Troopers (1997) x265 AAC 5.1 Bluray-1080p Tigole - [EHX].mkv → Starship Troopers (1997) x265 AAC 5.1 Bluray-1080p Tigole - [EHX].mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 346}
{"timestamp": "2025-12-31T20:30:27Z", "level": "INFO", "message": "\n✅ CONVERSION COMPLETE: Starship Troopers (1997) x265 AAC 5.1 Bluray-1080p Tigole - [EHX].mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 386}
{"timestamp": "2025-12-31T20:30:27Z", "level": "INFO", "message": " Type: MOVIE | Show: N/A", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 387}
{"timestamp": "2025-12-31T20:30:27Z", "level": "INFO", "message": " Size: 6522.97MB → 4495.6MB (68.9% of original, 31.1% reduction)", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 388}
{"timestamp": "2025-12-31T20:30:27Z", "level": "INFO", "message": " Method: CQ | Status: SUCCESS", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 389}
{"timestamp": "2025-12-31T20:30:28Z", "level": "INFO", "message": "Deleted original and processing copy for Starship Troopers (1997) x265 AAC 5.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "_save_successful_encoding", "line": 395}
{"timestamp": "2025-12-31T20:30:28Z", "level": "INFO", "message": "Processing: Death From Above.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T20:30:30Z", "level": "INFO", "message": "Copied Death From Above.mkv → Death From Above.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T20:30:30Z", "level": "INFO", "message": "Source resolution detected: 708x480", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T20:30:30Z", "level": "INFO", "message": "Source 708x480 (<=720p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 65}
{"timestamp": "2025-12-31T20:30:30Z", "level": "INFO", "message": "Source 708x480 (<=720p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 124}
{"timestamp": "2025-12-31T20:30:31Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T20:30:31Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T20:30:31Z", "level": "INFO", "message": " • Source Resolution: 708x480", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T20:30:31Z", "level": "INFO", "message": " • Target Resolution: 708x480", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T20:30:31Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T20:30:31Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T20:30:31Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T20:30:31Z", "level": "INFO", "message": " • CQ Value: 34", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T20:30:31Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T20:30:31Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 67kbps | Action: COPY (preserve) | Target: 67kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T20:30:31Z", "level": "INFO", "message": "Running CQ encode: Death From Above - [EHX].mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T20:31:08Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T20:31:08Z", "level": "INFO", "message": " Original Size: 209.73 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T20:31:08Z", "level": "INFO", "message": " Encoded Size: 189.88 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T20:31:08Z", "level": "INFO", "message": " Reduction: 90.5% of original (9.5% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T20:31:08Z", "level": "INFO", "message": " Resolution: 708x480 → 708x480", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T20:31:08Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T20:31:08Z", "level": "INFO", "message": "Processing: Deleted Scenes.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T20:31:08Z", "level": "INFO", "message": "Copied Deleted Scenes.mkv → Deleted Scenes.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T20:31:09Z", "level": "INFO", "message": "Source resolution detected: 710x346", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T20:31:09Z", "level": "INFO", "message": "Source 710x346 (<=720p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 65}
{"timestamp": "2025-12-31T20:31:09Z", "level": "INFO", "message": "Source 710x346 (<=720p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 124}
{"timestamp": "2025-12-31T20:31:09Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T20:31:09Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T20:31:09Z", "level": "INFO", "message": " • Source Resolution: 710x346", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T20:31:09Z", "level": "INFO", "message": " • Target Resolution: 710x346", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T20:31:09Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T20:31:09Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T20:31:09Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T20:31:09Z", "level": "INFO", "message": " • CQ Value: 34", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T20:31:09Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T20:31:09Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 67kbps | Action: COPY (preserve) | Target: 67kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T20:31:09Z", "level": "INFO", "message": "Running CQ encode: Deleted Scenes - [EHX].mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T20:31:19Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T20:31:19Z", "level": "INFO", "message": " Original Size: 49.76 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T20:31:19Z", "level": "INFO", "message": " Encoded Size: 37.43 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T20:31:19Z", "level": "INFO", "message": " Reduction: 75.2% of original (24.8% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T20:31:19Z", "level": "INFO", "message": " Resolution: 710x346 → 710x346", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T20:31:19Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T20:31:19Z", "level": "INFO", "message": "Processing: FX Comparisons.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T20:31:21Z", "level": "INFO", "message": "Copied FX Comparisons.mkv → FX Comparisons.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T20:31:21Z", "level": "INFO", "message": "Source resolution detected: 708x476", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T20:31:21Z", "level": "INFO", "message": "Source 708x476 (<=720p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 65}
{"timestamp": "2025-12-31T20:31:21Z", "level": "INFO", "message": "Source 708x476 (<=720p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 124}
{"timestamp": "2025-12-31T20:31:22Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T20:31:22Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T20:31:22Z", "level": "INFO", "message": " • Source Resolution: 708x476", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T20:31:22Z", "level": "INFO", "message": " • Target Resolution: 708x476", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T20:31:22Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T20:31:22Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T20:31:22Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T20:31:22Z", "level": "INFO", "message": " • CQ Value: 34", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T20:31:22Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T20:31:22Z", "level": "INFO", "message": " - Stream #1: 2ch→2ch | Lang: und | Detected: 64kbps | Action: COPY (preserve) | Target: 64kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T20:31:22Z", "level": "INFO", "message": "Running CQ encode: FX Comparisons - [EHX].mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}
{"timestamp": "2025-12-31T20:31:57Z", "level": "INFO", "message": "\n📊 ENCODE RESULTS:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 107}
{"timestamp": "2025-12-31T20:31:57Z", "level": "INFO", "message": " Original Size: 247.74 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 108}
{"timestamp": "2025-12-31T20:31:57Z", "level": "INFO", "message": " Encoded Size: 214.33 MB", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 109}
{"timestamp": "2025-12-31T20:31:57Z", "level": "INFO", "message": " Reduction: 86.5% of original (13.5% saved)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 110}
{"timestamp": "2025-12-31T20:31:57Z", "level": "INFO", "message": " Resolution: 708x476 → 708x476", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 111}
{"timestamp": "2025-12-31T20:31:57Z", "level": "INFO", "message": " Audio Streams: 1 streams processed", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 112}
{"timestamp": "2025-12-31T20:31:57Z", "level": "ERROR", "message": "3 consecutive failures. Stopping process.", "module": "process_manager", "funcName": "process_folder", "line": 189}
{"timestamp": "2025-12-31T20:31:57Z", "level": "INFO", "message": "Batch processing complete", "module": "process_manager", "funcName": "process_folder", "line": 336}
{"timestamp": "2025-12-31T20:31:57Z", "level": "INFO", "message": "Using path as-is: P:\\movies\\John Wick - Chapter 3 - Parabellum (2019)", "module": "main", "funcName": "normalize_input_path", "line": 49}
{"timestamp": "2025-12-31T20:31:57Z", "level": "INFO", "message": "Processing: John Wick - Chapter 3 - Parabellum (2019) x265 AAC 7.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 101}
{"timestamp": "2025-12-31T20:32:54Z", "level": "INFO", "message": "Copied John Wick - Chapter 3 - Parabellum (2019) x265 AAC 7.1 Bluray-1080p Tigole.mkv → John Wick - Chapter 3 - Parabellum (2019) x265 AAC 7.1 Bluray-1080p Tigole.mkv", "module": "process_manager", "funcName": "process_folder", "line": 106}
{"timestamp": "2025-12-31T20:32:54Z", "level": "INFO", "message": "Source resolution detected: 1920x800", "module": "video_handler", "funcName": "get_source_resolution", "line": 29}
{"timestamp": "2025-12-31T20:32:54Z", "level": "INFO", "message": "Source 1920x800 (<=1080p). Preserving source resolution.", "module": "video_handler", "funcName": "determine_target_resolution", "line": 68}
{"timestamp": "2025-12-31T20:32:54Z", "level": "INFO", "message": "Source 1920x800 (<=1080p). Preserving source resolution.", "module": "process_manager", "funcName": "process_folder", "line": 127}
{"timestamp": "2025-12-31T20:33:04Z", "level": "INFO", "message": "\n🧩 ENCODE SETTINGS", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 25}
{"timestamp": "2025-12-31T20:33:04Z", "level": "INFO", "message": " Video:", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 28}
{"timestamp": "2025-12-31T20:33:04Z", "level": "INFO", "message": " • Source Resolution: 1920x800", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 29}
{"timestamp": "2025-12-31T20:33:04Z", "level": "INFO", "message": " • Target Resolution: 1920x800", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 30}
{"timestamp": "2025-12-31T20:33:04Z", "level": "INFO", "message": " • Encoder: av1_nvenc (preset p1, pix_fmt p010le)", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 31}
{"timestamp": "2025-12-31T20:33:04Z", "level": "INFO", "message": " • Scale Filter: lanczos", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 32}
{"timestamp": "2025-12-31T20:33:04Z", "level": "INFO", "message": " • Encode Method: CQ", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 33}
{"timestamp": "2025-12-31T20:33:04Z", "level": "INFO", "message": " • CQ Value: 32", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 35}
{"timestamp": "2025-12-31T20:33:04Z", "level": "INFO", "message": " Audio Streams (1 detected):", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 42}
{"timestamp": "2025-12-31T20:33:04Z", "level": "INFO", "message": " - Stream #1: 8ch→6ch | Lang: und | Detected: 696kbps | Action: ENCODE | Target: 448kbps", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 60}
{"timestamp": "2025-12-31T20:33:04Z", "level": "INFO", "message": "Running CQ encode: John Wick - Chapter 3 - Parabellum (2019) x265 AAC 7.1 Bluray-1080p Tigole - [EHX].mkv", "module": "encode_engine", "funcName": "run_ffmpeg", "line": 98}

21
logs/failure.log Normal file
View File

@ -0,0 +1,21 @@
2025-12-31 12:42:36 | Pacific Rim (2013) x265 AAC 7.1 Bluray-1080p Tigole.mkv | CQ failed: Size threshold not met (71.6%)
2025-12-31 12:42:54 | Behind the Scenes - Drift Space.mkv | CQ failed: Size threshold not met (122.5%)
2025-12-31 13:02:27 | Behind the Scenes - Drift Space.mkv | CQ failed: Size threshold not met (122.5%)
2025-12-31 13:03:26 | Behind the Scenes - The Digital Artistry of Pacific Rim.mkv | CQ failed: Size threshold not met (180.9%)
2025-12-31 13:04:07 | Behind the Scenes - The Shatterdome.mkv | CQ failed: Size threshold not met (174.6%)
2025-12-31 13:40:45 | The Keyboard Cowboys - A Look Back at Hackers.mkv | CQ failed: Size threshold not met (135.1%)
2025-12-31 13:40:54 | Trailer.mkv | CQ failed: Size threshold not met (124.7%)
2025-12-31 14:02:32 | The Making of The Truman Show.mkv | CQ failed: Size threshold not met (117.3%)
2025-12-31 14:02:45 | The Visual Effects of The Truman Show.mkv | CQ failed: Size threshold not met (106.3%)
2025-12-31 14:02:51 | Product Placement.mkv | CQ failed: Size threshold not met (106.8%)
2025-12-31 14:24:35 | A Shot in the Dark.mkv | CQ failed: Size threshold not met (110.1%)
2025-12-31 14:24:52 | Chad and Keanu - Through Wick and Thin.mkv | CQ failed: Size threshold not met (119.8%)
2025-12-31 14:25:08 | In Honor of the Dead.mkv | CQ failed: Size threshold not met (123.7%)
2025-12-31 14:44:59 | The Lord of the Rings - The Return of the King (2003) x265 EAC3 5.1 Bluray-1080p GalaxyRG265.mkv | CQ failed: Size threshold not met (79.0%)
2025-12-31 15:09:01 | A Shot in the Dark.mkv | CQ failed: Size threshold not met (110.1%)
2025-12-31 15:09:19 | Chad and Keanu - Through Wick and Thin.mkv | CQ failed: Size threshold not met (119.8%)
2025-12-31 15:09:41 | In Honor of the Dead.mkv | CQ failed: Size threshold not met (123.7%)
2025-12-31 15:13:45 | Starship Troopers (1997) x265 AAC 5.1 Bluray-1080p Tigole.mkv | CQ error: Command '['ffmpeg', '-y', '-i', 'processing\\Starship Troopers (1997) x265 AAC 5.1 Bluray-1080p Tigo
2025-12-31 15:31:08 | Death From Above.mkv | CQ failed: Size threshold not met (90.5%)
2025-12-31 15:31:19 | Deleted Scenes.mkv | CQ failed: Size threshold not met (75.2%)
2025-12-31 15:31:57 | FX Comparisons.mkv | CQ failed: Size threshold not met (86.5%)

571
main.py
View File

@ -1,523 +1,116 @@
#!/usr/bin/env python3
"""
AV1 Batch Video Transcoder
Main entry point for batch video encoding with intelligent audio and resolution handling.
"""
import argparse
import csv
import json
import os
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
from core.config_helper import load_config_xml
from core.logger_helper import setup_logger
from core.process_manager import process_folder
# =============================
# Setup logger
# PATH NORMALIZATION
# =============================
def normalize_input_path(input_path: str, path_mappings: dict) -> Path:
"""
Normalize input path from various formats to Windows path.
Supports:
- Windows paths: "P:\\tv\\show" or "P:/tv/show"
- Linux paths: "/mnt/plex/tv/show" (maps to Windows equivalent if mapping exists)
- Mixed separators: "P:/tv\\show"
Args:
input_path: Path string from user input
path_mappings: Dict mapping Windows paths to Linux paths from config
Returns:
Path object pointing to the actual local folder
"""
# First, try to map Linux paths to Windows paths (reverse mapping)
# If user provides "/mnt/plex/tv", find the mapping and convert to "P:\\tv"
for win_path, linux_path in path_mappings.items():
if input_path.lower().startswith(linux_path.lower()):
# Found a matching Linux path, convert to Windows
relative = input_path[len(linux_path):].lstrip("/").lstrip("\\")
result = Path(win_path) / relative if relative else Path(win_path)
logger.info(f"Path mapping: {input_path} -> {result}")
print(f" Mapped Linux path {input_path} to {result}")
return result
# No mapping found, use path as-is (normalize separators to Windows)
# Convert forward slashes to backslashes for Windows
normalized = input_path.replace("/", "\\")
result = Path(normalized)
logger.info(f"Using path as-is: {result}")
return result
# =============================
# Setup
# =============================
LOG_FOLDER = Path(__file__).parent / "logs"
logger = setup_logger(LOG_FOLDER)
# =============================
# Tracker CSV
# =============================
TRACKER_FILE = Path(__file__).parent / "conversion_tracker.csv"
if not TRACKER_FILE.exists():
with open(TRACKER_FILE, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow([
"type","show","filename","original_size_MB","processed_size_MB","percentage","method"
"type", "show", "filename", "original_size_MB", "processed_size_MB", "percentage",
"source_resolution", "target_resolution", "audio_streams", "cq_value", "method"
])
# =============================
# AUDIO BUCKET LOGIC
# =============================
def calculate_stream_bitrate(input_file: Path, stream_index: int) -> int:
"""
Extract audio stream to temporary file using -c copy, capture bitrate from ffmpeg output.
Returns bitrate in kbps. Falls back to 0 (and uses metadata) if extraction fails.
Uses ffmpeg's reported bitrate which is more accurate than calculating from file size/duration.
"""
temp_fd, temp_audio_path = tempfile.mkstemp(suffix=".aac", dir=None)
os.close(temp_fd)
try:
# Step 1: Extract audio stream with -c copy (lossless extraction)
# ffmpeg outputs bitrate info to stderr
extract_cmd = [
"ffmpeg", "-y", "-i", str(input_file),
"-map", f"0:a:{stream_index}",
"-c", "copy",
temp_audio_path
]
logger.debug(f"Extracting audio stream {stream_index} to temporary file for bitrate calculation...")
result = subprocess.run(extract_cmd, capture_output=True, text=True, check=True)
# Step 2: Parse bitrate from ffmpeg's output (stderr)
# Look for line like: "bitrate= 457.7kbits/s"
bitrate_kbps = 0
for line in result.stderr.split("\n"):
if "bitrate=" in line:
# Extract bitrate value from line like "size= 352162KiB time=01:45:03.05 bitrate= 457.7kbits/s"
parts = line.split("bitrate=")
if len(parts) > 1:
bitrate_str = parts[1].strip().split("kbits/s")[0].strip()
try:
bitrate_kbps = int(float(bitrate_str))
logger.debug(f"Stream {stream_index}: Extracted bitrate from ffmpeg output: {bitrate_kbps} kbps")
break
except ValueError:
continue
# If we couldn't parse bitrate from output, fall back to calculation
if bitrate_kbps == 0:
logger.debug(f"Stream {stream_index}: Could not parse bitrate from ffmpeg output, calculating from file size...")
file_size_bytes = os.path.getsize(temp_audio_path)
# Get duration using ffprobe
duration_cmd = [
"ffprobe", "-v", "error",
"-show_entries", "format=duration",
"-of", "default=noprint_wrappers=1:nokey=1:noprint_wrappers=1",
temp_audio_path
]
duration_result = subprocess.run(duration_cmd, capture_output=True, text=True, check=True)
duration_seconds = float(duration_result.stdout.strip())
bitrate_kbps = int((file_size_bytes * 8) / duration_seconds / 1000)
logger.debug(f"Stream {stream_index}: Calculated bitrate from file: {bitrate_kbps} kbps")
return bitrate_kbps
except Exception as e:
logger.warning(f"Failed to calculate bitrate for stream {stream_index}: {e}. Will fall back to metadata.")
return 0
finally:
# Clean up temporary audio file
try:
if os.path.exists(temp_audio_path):
os.remove(temp_audio_path)
logger.debug(f"Deleted temporary audio file: {temp_audio_path}")
except Exception as e:
logger.warning(f"Could not delete temporary file {temp_audio_path}: {e}")
def choose_audio_bitrate(channels: int, bitrate_kbps: int, audio_config: dict, is_1080_class: bool) -> tuple:
"""
Choose audio codec and bitrate based on channel count, detected bitrate, and resolution.
Returns tuple: (codec, target_bitrate_bps)
- codec: "aac", "libopus", or "copy" (to preserve original without re-encoding)
- target_bitrate_bps: target bitrate in bits/sec (0 if using "copy")
Rules:
Stereo + 1080p:
- Above 192k high (192k) with AAC
- At/below 192k preserve (copy)
Stereo + 720p:
- Above 160k medium (160k) with AAC
- At/below 160k preserve (copy)
Multi-channel:
- Below 384k low (384k) with AAC
- 384k to below medium low (384k) with AAC
- Medium and above medium with AAC
"""
# Normalize to 2ch or 6ch output
output_channels = 6 if channels >= 6 else 2
if output_channels == 2:
# Stereo logic
if is_1080_class:
# 1080p+ stereo
high_br = audio_config["stereo"]["high"]
if bitrate_kbps > (high_br / 1000): # Above 192k
return ("aac", high_br)
else:
# Preserve original
return ("copy", 0)
else:
# 720p stereo
medium_br = audio_config["stereo"]["medium"]
if bitrate_kbps > (medium_br / 1000): # Above 160k
return ("aac", medium_br)
else:
# Preserve original
return ("copy", 0)
else:
# Multi-channel (6ch+) logic
low_br = audio_config["multi_channel"]["low"]
medium_br = audio_config["multi_channel"]["medium"]
if bitrate_kbps < (medium_br / 1000):
# Below medium, use low
return ("aac", low_br)
else:
# Medium and above, use medium
return ("aac", medium_br)
# =============================
# PATH NORMALIZATION
# =============================
def normalize_path_for_service(local_path: str, path_mappings: dict) -> str:
for win_path, linux_path in path_mappings.items():
if local_path.lower().startswith(win_path.lower()):
return local_path.replace(win_path, linux_path).replace("\\", "/")
return local_path.replace("\\", "/")
# =============================
# AUDIO STREAMS DETECTION
# =============================
def get_source_resolution(input_file: Path) -> tuple:
"""
Get source video resolution (width, height).
Returns tuple: (width, height)
"""
try:
cmd = [
"ffprobe", "-v", "error",
"-select_streams", "v:0",
"-show_entries", "stream=width,height",
"-of", "default=noprint_wrappers=1:nokey=1:noprint_wrappers=1",
str(input_file)
]
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
lines = result.stdout.strip().split("\n")
width = int(lines[0]) if len(lines) > 0 else 1920
height = int(lines[1]) if len(lines) > 1 else 1080
logger.info(f"Source resolution detected: {width}x{height}")
return (width, height)
except Exception as e:
logger.warning(f"Failed to detect source resolution: {e}. Defaulting to 1920x1080")
return (1920, 1080)
def get_audio_streams(input_file: Path):
"""
Detect audio streams and calculate robust bitrates by extracting each stream.
Returns list of (index, channels, calculated_bitrate_kbps, language, metadata_bitrate_kbps)
"""
cmd = [
"ffprobe","-v","error","-select_streams","a",
"-show_entries","stream=index,channels,bit_rate,tags=language",
"-of","json", str(input_file)
]
result = subprocess.run(cmd, capture_output=True, text=True)
data = json.loads(result.stdout)
streams = []
for stream_num, s in enumerate(data.get("streams", [])):
index = s["index"]
channels = s.get("channels", 2)
src_lang = s.get("tags", {}).get("language", "und")
bit_rate_meta = int(s.get("bit_rate", 0)) if s.get("bit_rate") else 0
# Calculate robust bitrate by extracting the audio stream
calculated_bitrate_kbps = calculate_stream_bitrate(input_file, stream_num)
# If calculation failed, fall back to metadata
if calculated_bitrate_kbps == 0:
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")
streams.append((index, channels, calculated_bitrate_kbps, src_lang, int(bit_rate_meta / 1000) if bit_rate_meta else 0))
return streams
# =============================
# FFmpeg ENCODE
# =============================
def run_ffmpeg(input_file: Path, output_file: Path, cq: int, scale_width: int, scale_height: int,
filter_flags: str, audio_config: dict, method: str, bitrate_config: dict):
# Get source resolution
src_width, src_height = get_source_resolution(input_file)
streams = get_audio_streams(input_file)
# Log comprehensive encode settings
header = f"\n🧩 ENCODE SETTINGS"
logger.info(header)
print(" ")
logger.info(f" Video:")
logger.info(f" • Source Resolution: {src_width}x{src_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" • Scale Filter: {filter_flags}")
logger.info(f" • Encode Method: {method}")
if method == "CQ":
logger.info(f" • CQ Value: {cq}")
else:
res_key = "1080" if scale_height >= 1080 or scale_width >= 1920 else "720"
vb = bitrate_config.get(f"bitrate_{res_key}", "900k")
maxrate = bitrate_config.get(f"maxrate_{res_key}", "1250k")
logger.info(f" • Bitrate: {vb}, Max: {maxrate}")
logger.info(f" Audio Streams ({len(streams)} detected):")
print(" ")
for (index, channels, avg_bitrate, src_lang, meta_bitrate) in streams:
# Normalize to 2ch or 6ch output
is_1080_class = scale_height >= 1080 or scale_width >= 1920
output_channels = 6 if is_1080_class and channels >= 6 else 2
codec, br = choose_audio_bitrate(output_channels, avg_bitrate, audio_config, is_1080_class)
if codec == "copy":
action = "COPY (preserve)"
bitrate_display = f"{avg_bitrate}kbps"
else:
action = "ENCODE"
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}"
print(line)
logger.info(line)
cmd = ["ffmpeg","-y","-i",str(input_file),
"-vf",f"scale={scale_width}:{scale_height}:flags={filter_flags}:force_original_aspect_ratio=decrease",
"-map","0:v","-map","0:a","-map","0:s?",
"-c:v","av1_nvenc","-preset","p1","-pix_fmt","p010le"]
if method=="CQ":
cmd += ["-cq", str(cq)]
else:
# Use bitrate config (fallback mode)
res_key = "1080" if scale_height >= 1080 or scale_width >= 1920 else "720"
vb = bitrate_config.get(f"bitrate_{res_key}", "900k")
maxrate = bitrate_config.get(f"maxrate_{res_key}", "1250k")
bufsize = bitrate_config.get(f"bufsize_{res_key}", "1800k")
cmd += ["-b:v", vb, "-maxrate", maxrate, "-bufsize", bufsize]
for i, (index, channels, avg_bitrate, src_lang, meta_bitrate) in enumerate(streams):
# Normalize to 2ch or 6ch output
is_1080_class = scale_height >= 1080 or scale_width >= 1920
output_channels = 6 if is_1080_class and channels >= 6 else 2
codec, br = choose_audio_bitrate(output_channels, avg_bitrate, audio_config, is_1080_class)
if codec == "copy":
# Preserve original audio
cmd += [f"-c:a:{i}", "copy"]
else:
# Re-encode with target bitrate
cmd += [
f"-c:a:{i}", codec,
f"-b:a:{i}", str(br),
f"-ac:{i}", str(output_channels),
f"-channel_layout:a:{i}", "5.1" if output_channels == 6 else "stereo"
]
cmd += ["-c:s","copy",str(output_file)]
print(f"\n🎬 Running {method} encode: {output_file.name}")
logger.info(f"Running {method} encode: {output_file.name}")
subprocess.run(cmd, check=True)
orig_size = input_file.stat().st_size
out_size = output_file.stat().st_size
reduction_ratio = out_size / orig_size
# Log comprehensive results
logger.info(f"\n📊 ENCODE RESULTS:")
logger.info(f" Original Size: {orig_size/1e6:.2f} MB")
logger.info(f" Encoded Size: {out_size/1e6:.2f} MB")
logger.info(f" Reduction: {reduction_ratio:.1%} of original ({(1-reduction_ratio):.1%} saved)")
logger.info(f" Resolution: {src_width}x{src_height}{scale_width}x{scale_height}")
logger.info(f" Audio Streams: {len(streams)} streams processed")
msg = f"📦 Original: {orig_size/1e6:.2f} MB → Encoded: {out_size/1e6:.2f} MB ({reduction_ratio:.1%} of original)"
print(msg)
return orig_size, out_size, reduction_ratio
# =============================
# PROCESS FOLDER
# =============================
def process_folder(folder: Path, cq: int, transcode_mode: str, resolution: str, config: dict):
if not folder.exists():
print(f"❌ Folder not found: {folder}")
logger.error(f"Folder not found: {folder}")
return
audio_config = config["audio"]
bitrate_config = config["encode"]["fallback"]
filters_config = config["encode"]["filters"]
suffix = config["suffix"]
extensions = config["extensions"]
ignore_tags = config["ignore_tags"]
reduction_ratio_threshold = config["reduction_ratio_threshold"]
# Resolution logic: explicit arg takes precedence, else use smart defaults
explicit_resolution = resolution # Will be None if not specified
filter_flags = filters_config.get("default","lanczos")
folder_lower = str(folder).lower()
is_tv = "\\tv\\" in folder_lower or "/tv/" in folder_lower
if is_tv:
filter_flags = filters_config.get("tv","bicubic")
processing_folder = Path(config["processing_folder"])
processing_folder.mkdir(parents=True, exist_ok=True)
# Track if we switch to bitrate mode
use_bitrate = True if transcode_mode == "bitrate" else False
for file in folder.rglob("*"):
if file.suffix.lower() not in extensions:
continue
if any(tag.lower() in file.name.lower() for tag in ignore_tags):
print(f"⏭️ Skipping: {file.name}")
logger.info(f"Skipping: {file.name}")
continue
print("="*60)
logger.info(f"Processing: {file.name}")
print(f"📁 Processing: {file.name}")
temp_input = processing_folder / file.name
shutil.copy2(file, temp_input)
logger.info(f"Copied {file.name}{temp_input.name}")
# Detect source resolution and determine target resolution
src_width, src_height = get_source_resolution(temp_input)
# Smart resolution logic
if explicit_resolution:
# User explicitly specified resolution - always use it
target_resolution = explicit_resolution
if target_resolution == "1080":
res_height = 1080
res_width = 1920
elif target_resolution == "720":
res_height = 720
res_width = 1280
else: # 480
res_height = 480
res_width = 854
logger.info(f"Using explicitly specified resolution: {res_width}x{res_height}")
else:
# No explicit resolution - use smart defaults
if src_height > 1080:
# Scale down anything above 1080p to 1080p
target_resolution = "1080"
res_height = 1080
res_width = 1920
print(f"⚠️ Source {src_width}x{src_height} is above 1080p. Scaling down to 1080p.")
logger.info(f"Source {src_width}x{src_height} detected. Scaling to 1080p.")
else:
# Preserve source resolution (480p, 720p, 1080p, etc.)
res_height = src_height
res_width = src_width
if src_height <= 720:
target_resolution = "720"
print(f" Source {src_width}x{src_height} is 720p or lower. Preserving resolution.")
logger.info(f"Source {src_width}x{src_height} (<=720p). Preserving source resolution.")
else:
target_resolution = "1080"
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.")
# Set CQ based on content type and target resolution
content_cq = config["encode"]["cq"].get(f"tv_{target_resolution}" if is_tv else f"movie_{target_resolution}", 32)
file_cq = cq if cq is not None else content_cq
temp_output = processing_folder / f"{file.stem}{suffix}{file.suffix}"
method = "Bitrate" if use_bitrate else "CQ"
try:
orig_size, out_size, reduction_ratio = run_ffmpeg(temp_input, temp_output, file_cq, res_width, res_height, filter_flags, audio_config, method, bitrate_config)
except subprocess.CalledProcessError as e:
print(f"❌ FFmpeg failed: {e}")
logger.error(f"FFmpeg failed: {e}")
temp_input.unlink(missing_ok=True)
break
if method=="CQ" and reduction_ratio>=reduction_ratio_threshold:
print(f"⚠️ CQ encode did not achieve target size ({reduction_ratio:.1%} >= {reduction_ratio_threshold:.1%}). Switching all remaining files to Bitrate.")
logger.warning(f"CQ encode failed target ({reduction_ratio:.1%}). Switching to Bitrate for remaining files.")
use_bitrate = True
try:
# Retry current file using bitrate
temp_output.unlink(missing_ok=True)
orig_size, out_size, reduction_ratio = run_ffmpeg(temp_input, temp_output, cq, res_width, res_height, filter_flags, audio_config, "Bitrate", bitrate_config)
if reduction_ratio>=reduction_ratio_threshold:
print(f"❌ Bitrate encode also failed target ({reduction_ratio:.1%}). Stopping process.")
logger.error(f"Bitrate encode failed target ({reduction_ratio:.1%}). Stopping process.")
temp_input.unlink(missing_ok=True)
break
except subprocess.CalledProcessError as e:
print(f"❌ Bitrate retry failed: {e}")
logger.error(f"Bitrate retry failed: {e}")
temp_input.unlink(missing_ok=True)
break
elif method=="Bitrate" and reduction_ratio>=reduction_ratio_threshold:
print(f"❌ Bitrate encode failed target ({reduction_ratio:.1%}). Stopping process.")
logger.error(f"Bitrate encode failed target ({reduction_ratio:.1%}). Stopping process.")
temp_input.unlink(missing_ok=True)
break
dest_file = file.parent / temp_output.name
shutil.move(temp_output, dest_file)
print(f"🚚 Moved {temp_output.name}{dest_file.name}")
logger.info(f"Moved {temp_output.name}{dest_file.name}")
folder_parts = [p.lower() for p in folder.parts]
if "tv" in folder_parts:
f_type = "tv"
tv_index = folder_parts.index("tv")
show = folder.parts[tv_index + 1] if len(folder.parts) > tv_index + 1 else "Unknown"
elif "anime" in folder_parts:
f_type = "anime"
anime_index = folder_parts.index("anime")
show = folder.parts[anime_index + 1] if len(folder.parts) > anime_index + 1 else "Unknown"
else:
f_type = "movie"
show = "N/A"
orig_size_mb = round(orig_size / 1e6, 2)
proc_size_mb = round(out_size / 1e6, 2)
percentage = round(proc_size_mb / orig_size_mb * 100, 1)
with open(TRACKER_FILE, "a", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow([f_type, show, dest_file.name, orig_size_mb, proc_size_mb, percentage, method])
# Enhanced logging with all conversion details
logger.info(f"\n✅ CONVERSION COMPLETE: {dest_file.name}")
logger.info(f" Type: {f_type.upper()} | Show: {show}")
logger.info(f" Size: {orig_size_mb}MB → {proc_size_mb}MB ({percentage}% of original, {100-percentage:.1f}% reduction)")
logger.info(f" Method: {method} | Status: SUCCESS")
print(f"📝 Logged conversion: {dest_file.name} ({percentage}%), method={method}")
try:
temp_input.unlink()
file.unlink()
logger.info(f"Deleted original and processing copy for {file.name}")
except Exception as e:
print(f"⚠️ Could not delete files: {e}")
logger.warning(f"Could not delete files: {e}")
# =============================
# MAIN
# =============================
def main():
parser = argparse.ArgumentParser(description="Batch encode videos with logging and tracker")
parser.add_argument("folder", help="Path to folder containing videos")
parser.add_argument("--cq", type=int, help="Override default CQ")
parser.add_argument("--m", "--mode", dest="transcode_mode", default="cq", choices=["cq","bitrate"], help="Encode mode (cq or bitrate)")
parser.add_argument("--r", "--resolution", dest="resolution", default=None, choices=["480","720","1080"], help="Force target resolution (if not specified, preserves source if <=1080p, else scales to 1080p)")
parser = argparse.ArgumentParser(
description="Batch AV1 encode videos with intelligent audio and resolution handling",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s "C:\\Videos\\Movies" # Smart mode (preserve resolution, 4K->1080p)
%(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\\Low-Res" --r 480 # Force 480p for low-res content
"""
)
parser.add_argument("folder", help="Input folder containing video files")
parser.add_argument("--cq", type=int, help="Override default CQ value")
parser.add_argument(
"--m", "--mode", dest="transcode_mode", default="cq",
choices=["cq", "bitrate"],
help="Encode mode: CQ (constant quality) or Bitrate mode"
)
parser.add_argument(
"--r", "--resolution", dest="resolution", default=None,
choices=["480", "720", "1080"],
help="Force target resolution (if not specified: 4K->1080p, else preserve)"
)
args = parser.parse_args()
# Load configuration
config_path = Path(__file__).parent / "config.xml"
config = load_config_xml(config_path)
process_folder(Path(args.folder), args.cq, args.transcode_mode, args.resolution, config)
# Normalize input path (handle Linux paths, mixed separators, etc.)
folder = normalize_input_path(args.folder, config.get("path_mappings", {}))
# Verify folder exists
if not folder.exists():
print(f"❌ Folder not found: {folder}")
logger.error(f"Folder not found: {folder}")
return
# Process folder
process_folder(folder, args.cq, args.transcode_mode, args.resolution, config, TRACKER_FILE)
if __name__ == "__main__":
main()

View File

@ -1,3 +1,11 @@
--r 720 --m bitrate "P:\tv\Rupaul's Drag Race UK"
--r 720 --m bitrate "P:\tv\Canada's Drag Race"
--r 720 --m bitrate "P:\tv\Canada's Drag Race vs The World"
--m cq "P:\movies\Starship Troopers (1997)"
--m cq "P:\movies\John Wick - Chapter 3 - Parabellum (2019)"
--m cq "P:\movies\John Wick - Chapter 2 (2017)"
--m cq "P:\movies\Belle (2021)"
--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)"

View File

@ -1,18 +1,47 @@
@echo off
REM ====================================================================
REM Batch Transcode Queue Runner
REM Reads paths.txt and processes each line as a separate encode job
REM Each line should be a Python command with arguments, e.g.:
REM --r 720 --m bitrate "C:\Videos\TV Show"
REM --r 1080 --cq 28 "C:\Videos\Movies"
REM ====================================================================
setlocal enabledelayedexpansion
echo.
echo ====================================================================
echo Starting Batch Transcode Queue
echo ====================================================================
echo.
set "JOB_COUNT=0"
set "SUCCESS_COUNT=0"
set "FAILED_COUNT=0"
for /f "usebackq delims=" %%i in ("paths.txt") do (
echo ==============================
echo Processing: %%i
echo ==============================
set /a JOB_COUNT+=1
echo.
echo [Job !JOB_COUNT!] Processing: %%i
echo ======================================
py main.py %%i
if errorlevel 1 (
echo ERROR processing %%i
echo Continuing to next item...
set /a FAILED_COUNT+=1
echo [Job !JOB_COUNT!] FAILED - Continuing to next item...
) else (
set /a SUCCESS_COUNT+=1
echo [Job !JOB_COUNT!] SUCCESS
)
)
echo.
echo All jobs finished.
echo ====================================================================
echo Batch Transcode Queue Complete
echo ====================================================================
echo Total Jobs: !JOB_COUNT!
echo Successful: !SUCCESS_COUNT!
echo Failed: !FAILED_COUNT!
echo ====================================================================
pause