From 65ddc2dfde2bc6624c02eaaae0ec6871c063de1c Mon Sep 17 00:00:00 2001 From: mappu Date: Sun, 22 Oct 2017 00:00:00 +1300 Subject: [PATCH] (rebuilding history) v3.0 Rewritten in PHP Feature: Many more features now available via command-line arguments --- _shunt_tags2.sh | 39 --- shunt_tags | 637 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 637 insertions(+), 39 deletions(-) delete mode 100755 _shunt_tags2.sh create mode 100755 shunt_tags diff --git a/_shunt_tags2.sh b/_shunt_tags2.sh deleted file mode 100755 index cda1435..0000000 --- a/_shunt_tags2.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -while (( "$#" )); do - - if [[ $1 == "--graphical" ]] ; then - shift - xfce4-terminal -x bash $(basename "$0") "$@" - exit 0 - - elif [[ -d $1 ]] ; then - cd $1 - - else - echo "Unknown argument" >&2 - exit 1 - - fi - - shift - -done - -rren -r '*.mkv' _ " " -rren -e '[*.mkv' "\[(.+?)\]\s+([^\[]+)\s+(?:\[(.*)\])?\.([a-z0-9]{3})" "\2 [\1 \3].\4" -rren -r '*.mkv' "] [" " " -rren -r '*.mkv' "][" " " - -pause - -## -## Original _shunt_tags.cmd content: -## -## @rren -r *.mkv _ " " -## @rren -e [*.mkv "\[([^]+?)\]\s+([^\[]+)\s+(?:\[(.*)\])?\.([a-z0-9]{3})" "$2 [$1 $3].$4" -## @rren -r *.mkv "] [" " " -## @rren -r *.mkv "][" " " -## @pause -## - diff --git a/shunt_tags b/shunt_tags new file mode 100755 index 0000000..8187bef --- /dev/null +++ b/shunt_tags @@ -0,0 +1,637 @@ +#!/usr/bin/php +[, not an important tag anyway + $tmp = preg_replace('~\(([^\)]*(?:G_P|B-A|R2J|NakamaSub|720|1080|264|BD|DVD|AAC|AC3|Dual Audio|XVID|divx|[A-F0-9]{8})[^\)]*)\)~i', '[\1]', $tmp); + + // Work around nightmare sub group name + $tmp = str_replace('[Saiyan]BrollY', 'SaiyanBrollY', $tmp); + + // Move tags from front to end + if ($tmp[0] == '[') { + $tmp = preg_replace('~^(\[.+?\])\s?([^\[]+)(.*)$~', '\2 \1 \3', $tmp); + } + + // Cleanup + $cleanup = [ + '][' => ' ', + '] [' => ' ', + '] - [' => ' ', + ' ' => ' ', + ' - [' => '[', + '()' => '', + '( )' => '', + '720x480' => '480p', + '1280x720' => '720p', + '1920x1080' => '1080p', + 'BluRay' => 'BD', + 'Blu-Ray' => 'BD', + 'BDrip' => 'BD', + 'bd-rip' => 'BD', + 'Clean Ending' => 'NCED', + 'Clean Opening' => 'NCOP', + 'Creditless Opening' => 'NCOP', + 'Creditless Ending' => 'NCED', + 'Creditless OP' => 'NCOP', + 'Creditless ED' => 'NCED', + ]; + $tmp = $fixed_point_iterate($tmp, function($tmp) use ($cleanup) { + + // Clean up tags + $tmp = str_replace(array_keys($cleanup), array_values($cleanup), $tmp); + + // Move tags from middle to end + $tmp = preg_replace('~([^\[\]]+)(\[[^\]]+?\])([^\[\]]+)~', '\1\3\2', $tmp); + + return $tmp; + }); + /* + // Move tags from middle to end + $tmp = $fixed_point_iterate($tmp, function($tmp) { + return preg_replace('~([^\[\]]+)(\[[^\]]+?\])([^\[\]]+)~', '\1\3\2', $tmp); + }); + */ + // Cleanup commas inside tags ( [720p,Bluray] => [720p Bluray] ) + $tmp = preg_replace_callback('~\[(.+)\]~', function($matches) { return str_replace(',', ' ', $matches[0]); }, $tmp); + + // Remove trailing spaces before file extension + $tmp = trim($tmp); //$tmp = preg_replace('~\s+(\..{3})$~', '\1', $tmp); + + // Episode numbers + // #xx, Exx, EPxx, Ep.XX, Ep. XX, EXXv2... but not a year like 1970 or 2013 + + $add_episode_hyphen_if_not_year = function($matches) { + $is_year = (intval($matches[1]) > 1000); + if ($is_year) { + return $matches[0]; // as-is + } else { + return ' - '.$matches[1]; + } + }; + + $tmp = preg_replace_callback('~ (?:#|E(?:P(?:\. ?)?)?)(\d[\dv]*)\b~i', $add_episode_hyphen_if_not_year, $tmp); + + $got_episode_number = function($tmp) { + if (preg_match('~ \- S?[\d]+~', $tmp)) { + return true; + } + + // Allow "Lesson XX" (GTO format), "OVA XX", "ONA XX" + if (preg_match('~ \- (?:Lesson|OVA|ONA) [\d]+~', $tmp)) { + return true; + } + + return false; + }; + + if (! $got_episode_number($tmp)) { + $tmp = preg_replace_callback('~ episode (\d+)~', function($matches) { return sprintf(' - %02d', $matches[1]); }, $tmp); + } + + if (! $got_episode_number($tmp)) { + // Last ditch - replace any lone 1+ digit number (but also support v2 and 0.5 episodes) + $tmp = preg_replace_callback('~ (\d\d*(?:[v\.]\d\d?)?)\b~', $add_episode_hyphen_if_not_year, $tmp); + } + + // Two-digit episode numbers even for 0.5 and 6.75 episodes + $tmp = preg_replace('~ - (\d)\b~', ' - 0\1', $tmp); + $tmp = preg_replace('~ - (\d\.\d\d?)\b~', ' - 0\1', $tmp); + + // Anything after the episode number but before the tag is an episode title + // Enforce dash between them if it's not there already + $tmp = preg_replace('~ - (\d[\dv\.]+) ([^\[\-\(])~', ' - \1 - \2', $tmp); + + // Fix double dashes + //$tmp = str_replace('- -', '-', $tmp); + + // Remove unnecessary quotes + $tmp = preg_replace("~ - '(.+?)' ([-\\[])~", ' - \1 \2', $tmp); + + // From the first [ to the last ], no other [] should appear within, and there should be no extraneous spaces + $tmp = preg_replace_callback('~^([^\[]*?)\[(.+)\]~', function($matches) { + return $matches[1].'['.trim(str_replace(['[', ']', ' ', ' - ', '- ', '.'], [' ', ' ', ' ', ' ', ' ', ' '], $matches[2])).']'; + }, $tmp); + + // There's always a space before the tag + $tmp = preg_replace('~([^ ])\[~', '\1 [', $tmp); + + // Fixup [Saiyan]BrollY, that's a sub group + $tmp = str_replace('SaiyanBrollY', '[Saiyan]BrollY', $tmp); + + // Fixup terrible case with [m.3.3.w] sub group being detected as an episode number + // It would be preferable for this to not happen in the first place + $tmp = str_replace('m 03 3 w', 'm.3.3.w', $tmp); + + // Fixup anime_fin, that's a sub group + $tmp = str_replace('[anime fin', '[anime_fin', $tmp); + + // Fixup "No. 6", that's a show title + $tmp = str_replace('No. - 06 - ', 'No. 6 - ', $tmp); + + // Fixup "Oedo 808", that's a show title + $tmp = str_replace('Oedo - 808', 'Oedo 808', $tmp); + + // Fixup "h264-720p" + $tmp = str_replace("h264-", "h264 ", $tmp); + + // Reinstate file extension + $tmp = $tmp.$extension; + + // Done + return $tmp; +} + +function tests() { + $tests = [ + '[gg]_Valvrave_the_Liberator_-_12_[F9F3F5C5].mkv' + => 'Valvrave the Liberator - 12 [gg F9F3F5C5].mkv' + , + 'Poyopoyo Kansatsu Nikki - 16 [HorribleSubs][FShahid][46B9A7B5].mkv' + => 'Poyopoyo Kansatsu Nikki - 16 [HorribleSubs FShahid 46B9A7B5].mkv' + , + '[AHQ] Gundam Seed - 42 - Lacus Strikes.mkv' + => 'Gundam Seed - 42 - Lacus Strikes [AHQ].mkv' + , + '[Frenchies-Mux]_True_Mazinger_Impact!_Chapter_Z_03_(720p)[7EBA0032].mkv' + => 'True Mazinger Impact! Chapter Z - 03 [Frenchies-Mux 720p 7EBA0032].mkv' + , + '[RUELL-Next] Natsume Yuujinchou S4 EP09 (BD 1280x720 x264 AAC ASS(EN)) [BE846FE4].mkv' + => 'Natsume Yuujinchou S4 - 09 [RUELL-Next BD 720p x264 AAC BE846FE4].mkv' + , + '[Nubles] Space Battleship Yamato 2199 (2012) episode 21 [720p 10 bit AAC][C6884514].mkv' + => 'Space Battleship Yamato 2199 (2012) - 21 [Nubles 720p 10 bit AAC C6884514].mkv', + + '[Nubles] Space Battleship Yamato 2199 (2012) episode 8 (720p 10 bit AAC).mkv' + => 'Space Battleship Yamato 2199 (2012) - 08 [Nubles 720p 10 bit AAC].mkv' + , + "Salaryman_Kintaro_-_15_[A-Et]_(0F83C5FF).mkv" + => "Salaryman Kintaro - 15 [A-Et 0F83C5FF].mkv" + , + '[Elysium]Shiki.EP15(BD.720p.Hi10P.AAC)[0437F1F6].mkv' + => 'Shiki - 15 [Elysium BD 720p Hi10P AAC 0437F1F6].mkv' + , + '[Elysium]Shiki.EP20.5(BD.720p.Hi10P.AAC)[BFE4B6BF].mkv' + => 'Shiki - 20.5 [Elysium BD 720p Hi10P AAC BFE4B6BF].mkv' + , + '(G_P) Phoenix 03(x264)(17A173E7).mkv' + => 'Phoenix - 03 [G_P x264 17A173E7].mkv' + , + '[Commie] Teekyuu - OVA 1 [BD 720p AAC] [58F19BF2].mkv' + => 'Teekyuu - OVA 1 [Commie BD 720p AAC 58F19BF2].mkv' + , + '[Kametsu]_Your_Lie_in_April_11v2_[Blu-Ray][720p][Hi10][588D1B1F].mkv' + => 'Your Lie in April - 11 [Kametsu v2 BD 720p Hi10 588D1B1F].mkv' + , + 'Patlabor.TV.38v2.(Dual.Audio).XVID.[AM].ogm' + => 'Patlabor TV - 38 [v2 Dual Audio XVID AM].ogm' + , + 'Laputa_Castle_in_the_Sky_(1986)_[720p,BluRay,x264]_-_THORA.mkv' + => 'Laputa Castle in the Sky (1986) [720p BD x264 THORA].mkv' + , + '[HorribleSubs] 91 Days - 7.5 [720p].mkv' + => '91 Days - 07.5 [HorribleSubs 720p].mkv' + , + 'Code_Geass_Ep01_The_Day_the_Demon_was_Born_[720p,BluRay,x264]_-_gg-THORA.mkv' + => 'Code Geass - 01 - The Day the Demon was Born [720p BD x264 gg-THORA].mkv' + , + '[Z-Z] Blood + Ep. 01 - First Kiss.mkv' + => 'Blood + - 01 - First Kiss [Z-Z].mkv' + , + 'Code_Geass_Picture_Drama_6.75_[720p,BluRay,x264]_-_gg-THORA v2.mkv' + => 'Code Geass Picture Drama - 06.75 [720p BD x264 gg-THORA v2].mkv' + , + 'Baccano - 14 (OVA).mkv' + => 'Baccano - 14 (OVA).mkv' // no change expected + , + '[Jarzka] Cromartie High School 05 - Sentimental Bus [480p 10bit X264 DVD Dual-Audio] [A63E3F52].mkv' + => 'Cromartie High School - 05 - Sentimental Bus [Jarzka 480p 10bit X264 DVD Dual-Audio A63E3F52].mkv' + , + 'Area 88 OVA - 01 [Blitz flawed 5EF8A0E2].mkv' + => 'Area 88 OVA - 01 [Blitz flawed 5EF8A0E2].mkv' // no change expected + , + '[m.3.3.w]_Genshiken_Nidaime_no_Roku_-_OAD_[v0][A6EF7886].mkv' + => 'Genshiken Nidaime no Roku - OAD [m.3.3.w v0 A6EF7886].mkv' + , + 'Gintama - 21 [Rumbel XviD 3E184082 v2].avi' + => 'Gintama - 21 [Rumbel XviD 3E184082 v2].avi' // no change expected + , + 'Patlabor 1 (1989) [THORA].mkv' + => 'Patlabor - 01 (1989) [THORA].mkv' + , + '[ACX]Immortal_Grand_Prix_-_01_-_Time_to_Shine_[[Saiyan]BrollY]_[7A73D794].mkv' + => 'Immortal Grand Prix - 01 - Time to Shine [ACX [Saiyan]BrollY 7A73D794].mkv' + , + '[AnimeNOW] Danshi Koukousei no Nichijou - OP (BD 1280x720 10-bit x264 AAC) [429C5783].mkv' + => 'Danshi Koukousei no Nichijou - OP [AnimeNOW BD 720p 10-bit x264 AAC 429C5783].mkv' + , + '(B-A)Great_Teacher_Onizuka_-_Lesson_01_(6F68CC7E).mkv' + => 'Great Teacher Onizuka - Lesson 01 [B-A 6F68CC7E].mkv' + , + "[OZC]Mobile Suit Gundam - The 08th MS Team Blu-ray Box E01 'War for Two' [720p].mkv" + => "Mobile Suit Gundam - The 08th MS Team Blu-ray Box - 01 - War for Two [OZC 720p].mkv" + , + 'Gundam SEED Destiny 14[AEF97E04].avi' + => 'Gundam SEED Destiny - 14 [AEF97E04].avi' + , + 'Gundam SEED Destiny 1.avi' + => 'Gundam SEED Destiny - 01.avi' + , + '[Exiled-Destiny]_Girls_Bravo_Ep01v3_[R2_Video]_(94D81F22).mkv' + => 'Girls Bravo - 01 [Exiled-Destiny v3 R2 Video 94D81F22].mkv' + , + '[ACX]Immortal_Grand_Prix_-_11_-_And_Then_..._[[Saiyan]BrollY]_[50EC0CBC].mkv' + => 'Immortal Grand Prix - 11 - And Then ... [ACX [Saiyan]BrollY 50EC0CBC].mkv' + , + '[DmonHiro] Kyousougiga #00v2 - Recap (BD, 720p) [47986C5A].mkv' + => 'Kyousougiga - 00 - Recap [DmonHiro v2 BD 720p 47986C5A].mkv' + , + 'Legend.of.the.Galactic.Heroes.110.[x264.720p.10bit.AAC].mkv' + => 'Legend of the Galactic Heroes - 110 [x264 720p 10bit AAC].mkv' + , + 'Minipato OVA [anime_fin 2906CE7D].avi' + => 'Minipato OVA [anime_fin 2906CE7D].avi' // no change expected + , + '[Doki] No. 6 - NCOP (1280x720 h264 BD AAC) [0A4A8A9B].mkv' + => 'No. 6 - NCOP [Doki 720p h264 BD AAC 0A4A8A9B].mkv' + , + '[RG Genshiken] Gintama - Creditless Ending ep.088-095, 097-099 [DVDRip 704x528 x264 PCM].mkv' + => 'Gintama - NCED - 088-095, 097-099 [RG Genshiken DVDRip 704x528 x264 PCM].mkv' + , + 'Cyber City Oedo 808 - Art Gallery (XviD)[anibalance][4C7F6645].mkv' + => 'Cyber City Oedo 808 - Art Gallery [XviD anibalance 4C7F6645].mkv' + , + 'Death Billiards (Anime Mirai 2013) [gg 29BE9711].mkv' + => 'Death Billiards (Anime Mirai 2013) [gg 29BE9711].mkv' // no change expected + , + 'King of Thorn (Ibara no Ou) [Harth-QTS-v2].mkv' + => 'King of Thorn (Ibara no Ou) [Harth-QTS v2].mkv' + , + 'Lupin III - [fong] Lupin III - Kutabare! Nostradamus [BDrip.720p.10bit.DualAudio].mkv' + => 'Lupin III Lupin III - Kutabare! Nostradamus [fong BD 720p 10bit DualAudio].mkv' + , + "[JoJo]_Jojo's_Bizarre_Adventure_-_20_[BD][h264-720p_AAC][054C181A].mkv" + => "Jojo's Bizarre Adventure - 20 [JoJo BD h264 720p AAC 054C181A].mkv" + , + "Ghost in the Shell Arise - 04 [THORA 1080p].mkv" + => "Ghost in the Shell Arise - 04 [THORA 1080p].mkv" // no change expected + , + "Cowboy_Bebop_Knockin'_on_Heaven's_Door_(2001)_[1080p,BluRay,x264,flac]_-_THORA.mkv" + => "Cowboy Bebop Knockin' on Heaven's Door (2001) [1080p BD x264 flac THORA].mkv" + , + '[EG]Turn-A_Gundam_01_V2[E8F575A6].mkv' + => 'Turn-A Gundam - 01 [EG v2 E8F575A6].mkv' + , + 'PV2.mp4' + => 'PV2.mp4' // no change expected + , + '[DmonHiro] Kyousougiga - Clean Ending (v2) (BD, 720p) [F1849601].mkv' + => 'Kyousougiga - NCED [DmonHiro v2 BD 720p F1849601].mkv' + , + ]; + + $any_failures = false; + + foreach($tests as $input => $expected) { + $result = fixup($input); + if ($result !== $expected) { + echo "Test failed\n"; + echo " Input : $input \n"; + echo " Got : $result \n"; + echo " Expected : $expected \n"; + $any_failures = true; + } + } + + if (! $any_failures) { + echo "All tests passed.\n"; + } + + return !$any_failures; +} + +function findFilesNeedingReplacement($basedir, $recursive, $quiet) { + + $iterator = $recursive + ? new RecursiveIteratorIterator(new RecursiveDirectoryIterator($basedir), RecursiveIteratorIterator::SELF_FIRST) + : new DirectoryIterator($basedir) + ; + + if (! $quiet) { + echo "Scanning..."; + } + $ct = 0; + + $replacements = []; + + foreach ($iterator as $file) { + if (! $quiet) { + if (++$ct % 100 == 0) { + echo "."; + } + } + + if ($file->isDir()) { + continue; + } + + $fname = $file->getFilename(); + + if (! preg_match("~\.(?:mkv|mp4|avi|ogm|ogv)$~", $fname)) { + continue; + } + + $new = fixup($fname); + if ($new !== $fname) { + $replacements[] = [ + $file->getPath(), + $fname, + $new + ]; + } + + } + + if (! $quiet) { + echo "\n"; + } + + // Alphabetic sort + usort($replacements, function($a, $b) { + $pc = strcasecmp($a[0], $b[0]); + if ($pc !== 0) { + return $pc; + } + + return strcasecmp($a[1], $b[1]); + }); + + return $replacements; +} + +function displayReplacementsTable(array $replacements) { + $max_length = 10; + foreach($replacements as $r) { + $display_width = mb_strwidth($r[1], 'UTF-8'); // strlen($r[1]) + $max_length = max($max_length, $display_width); + } + + foreach($replacements as $r) { + echo sprintf("%-{$max_length}s %s\n", $r[1], $r[2]); + } +} + +function applyReplacements(array $replacements, $echo_undo_script) { + $any_errors = false; + + if ($echo_undo_script) { + echo "#!/bin/bash\nset -eu\n\n"; + } + + foreach($replacements as $r) { + $src_path = $r[0].'/'.$r[1]; + $dest_path = $r[0].'/'.$r[2]; + + if (file_exists($dest_path)) { + if ($echo_undo_script) { + echo "# "; + } + echo "Skipping {$r[1]} (destination path already exists)\n"; + $any_errors = true; + continue; + } + + rename($src_path, $dest_path); + if ($echo_undo_script) { + echo "mv ".escapeshellarg($dest_path)." ".escapeshellarg($src_path)."\n"; + } + } + + return $any_errors; +} + +function usage() { + echo << "; + $line = trim(fgets(STDIN)); + if ($line !== "y") { + echo "Not applying changes.\n"; + die(0); + } + } + + $any_errors = applyReplacements($replacements, $echo_undo_script); + + die($any_errors ? 1 : 0); +} + +main(array_slice($_SERVER['argv'], 1));