diff --git a/lib/guessit/test/__init__.py b/lib/guessit/test/__init__.py new file mode 100644 index 00000000..650a3116 --- /dev/null +++ b/lib/guessit/test/__init__.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# GuessIt - A library for guessing information from filenames +# Copyright (c) 2013 Nicolas Wack +# +# GuessIt is free software; you can redistribute it and/or modify it under +# the terms of the Lesser GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GuessIt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Lesser GNU General Public License for more details. +# +# You should have received a copy of the Lesser GNU General Public License +# along with this program. If not, see . +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +import logging +from guessit.slogging import setupLogging +setupLogging() +logging.disable(logging.INFO) diff --git a/lib/guessit/test/__main__.py b/lib/guessit/test/__main__.py new file mode 100644 index 00000000..32b8dd10 --- /dev/null +++ b/lib/guessit/test/__main__.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# GuessIt - A library for guessing information from filenames +# Copyright (c) 2013 Nicolas Wack +# +# GuessIt is free software; you can redistribute it and/or modify it under +# the terms of the Lesser GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GuessIt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Lesser GNU General Public License for more details. +# +# You should have received a copy of the Lesser GNU General Public License +# along with this program. If not, see . +# + +from __future__ import absolute_import, division, print_function, unicode_literals +from guessit.test import (test_api, test_autodetect, test_autodetect_all, test_doctests, + test_episode, test_hashes, test_language, test_main, + test_matchtree, test_movie, test_quality, test_utils) +from unittest import TextTestRunner + + +import logging + +def main(): + for suite in [test_api.suite, test_autodetect.suite, + test_autodetect_all.suite, test_doctests.suite, + test_episode.suite, test_hashes.suite, test_language.suite, + test_main.suite, test_matchtree.suite, test_movie.suite, + test_quality.suite, test_utils.suite]: + TextTestRunner(verbosity=2).run(suite) + + +if __name__ == '__main__': + main() diff --git a/lib/guessit/test/autodetect.yaml b/lib/guessit/test/autodetect.yaml new file mode 100644 index 00000000..c2da5ba8 --- /dev/null +++ b/lib/guessit/test/autodetect.yaml @@ -0,0 +1,289 @@ +? Movies/Fear and Loathing in Las Vegas (1998)/Fear.and.Loathing.in.Las.Vegas.720p.HDDVD.DTS.x264-ESiR.mkv +: type: movie + title: Fear and Loathing in Las Vegas + year: 1998 + screenSize: 720p + format: HD-DVD + audioCodec: DTS + videoCodec: h264 + releaseGroup: ESiR + +? Leopard.dmg +: type: unknown + extension: dmg + +? Series/Duckman/Duckman - 101 (01) - 20021107 - I, Duckman.avi +: type: episode + series: Duckman + season: 1 + episodeNumber: 1 + title: I, Duckman + date: 2002-11-07 + +? Series/Neverwhere/Neverwhere.05.Down.Street.[tvu.org.ru].avi +: type: episode + series: Neverwhere + episodeNumber: 5 + title: Down Street + website: tvu.org.ru + +? Neverwhere.05.Down.Street.[tvu.org.ru].avi +: type: episode + series: Neverwhere + episodeNumber: 5 + title: Down Street + website: tvu.org.ru + +? Series/Breaking Bad/Minisodes/Breaking.Bad.(Minisodes).01.Good.Cop.Bad.Cop.WEBRip.XviD.avi +: type: episode + series: Breaking Bad + episodeFormat: Minisode + episodeNumber: 1 + title: Good Cop Bad Cop + format: WEBRip + videoCodec: XviD + +? Series/Kaamelott/Kaamelott - Livre V - Ep 23 - Le Forfait.avi +: type: episode + series: Kaamelott + episodeNumber: 23 + title: Le Forfait + +? Movies/The Doors (1991)/09.03.08.The.Doors.(1991).BDRip.720p.AC3.X264-HiS@SiLUHD-English.[sharethefiles.com].mkv +: type: movie + title: The Doors + year: 1991 + date: 2008-03-09 + format: BluRay + screenSize: 720p + audioCodec: AC3 + videoCodec: h264 + releaseGroup: HiS@SiLUHD + language: english + website: sharethefiles.com + +? Movies/M.A.S.H. (1970)/MASH.(1970).[Divx.5.02][Dual-Subtitulos][DVDRip].ogm +: type: movie + title: M.A.S.H. + year: 1970 + videoCodec: DivX + format: DVD + +? the.mentalist.501.hdtv-lol.mp4 +: type: episode + series: The Mentalist + season: 5 + episodeNumber: 1 + format: HDTV + releaseGroup: LOL + +? the.simpsons.2401.hdtv-lol.mp4 +: type: episode + series: The Simpsons + season: 24 + episodeNumber: 1 + format: HDTV + releaseGroup: LOL + +? Homeland.S02E01.HDTV.x264-EVOLVE.mp4 +: type: episode + series: Homeland + season: 2 + episodeNumber: 1 + format: HDTV + videoCodec: h264 + releaseGroup: EVOLVE + +? /media/Band_of_Brothers-e01-Currahee.mkv +: type: episode + series: Band of Brothers + episodeNumber: 1 + title: Currahee + +? /media/Band_of_Brothers-x02-We_Stand_Alone_Together.mkv +: type: episode + series: Band of Brothers + bonusNumber: 2 + bonusTitle: We Stand Alone Together + +? /movies/James_Bond-f21-Casino_Royale-x02-Stunts.mkv +: type: movie + title: Casino Royale + filmSeries: James Bond + filmNumber: 21 + bonusNumber: 2 + bonusTitle: Stunts + +? /TV Shows/new.girl.117.hdtv-lol.mp4 +: type: episode + series: New Girl + season: 1 + episodeNumber: 17 + format: HDTV + releaseGroup: LOL + +? The.Office.(US).1x03.Health.Care.HDTV.XviD-LOL.avi +: type: episode + series: The Office (US) + country: US + season: 1 + episodeNumber: 3 + title: Health Care + format: HDTV + videoCodec: XviD + releaseGroup: LOL + +? The_Insider-(1999)-x02-60_Minutes_Interview-1996.mp4 +: type: movie + title: The Insider + year: 1999 + bonusNumber: 2 + bonusTitle: 60 Minutes Interview-1996 + +? OSS_117--Cairo,_Nest_of_Spies.mkv +: type: movie + title: OSS 117--Cairo, Nest of Spies + +? Rush.._Beyond_The_Lighted_Stage-x09-Between_Sun_and_Moon-2002_Hartford.mkv +: type: movie + title: Rush Beyond The Lighted Stage + bonusNumber: 9 + bonusTitle: Between Sun and Moon-2002 Hartford + +? House.Hunters.International.S56E06.720p.hdtv.x264.mp4 +: type: episode + series: House Hunters International + season: 56 + episodeNumber: 6 + screenSize: 720p + format: HDTV + videoCodec: h264 + +? White.House.Down.2013.1080p.BluRay.DTS-HD.MA.5.1.x264-PublicHD.mkv +: type: movie + title: White House Down + year: 2013 + screenSize: 1080p + format: BluRay + audioCodec: DTS + audioProfile: HDMA + videoCodec: h264 + releaseGroup: PublicHD + audioChannels: "5.1" + +? Hostages.S01E01.Pilot.for.Air.720p.WEB-DL.DD5.1.H.264-NTb.nfo +: type: episodeinfo + series: Hostages + title: Pilot for Air + season: 1 + episodeNumber: 1 + screenSize: 720p + format: WEB-DL + audioChannels: "5.1" + videoCodec: h264 + audioCodec: DolbyDigital + releaseGroup: NTb + +? Despicable.Me.2.2013.1080p.BluRay.x264-VeDeTT.nfo +: type: movieinfo + title: Despicable Me 2 + year: 2013 + screenSize: 1080p + format: BluRay + videoCodec: h264 + releaseGroup: VeDeTT + +? Le Cinquieme Commando 1971 SUBFORCED FRENCH DVDRiP XViD AC3 Bandix.mkv +: type: movie + audioCodec: AC3 + format: DVD + releaseGroup: Bandix + subtitleLanguage: French + title: Le Cinquieme Commando + videoCodec: XviD + year: 1971 + +? Le Seigneur des Anneaux - La Communauté de l'Anneau - Version Longue - BDRip.mkv +: type: movie + format: BluRay + title: Le Seigneur des Anneaux + +? La petite bande (Michel Deville - 1983) VF PAL MP4 x264 AAC.mkv +: type: movie + audioCodec: AAC + language: French + title: La petite bande + videoCodec: h264 + year: 1983 + +? Retour de Flammes (Gregor Schnitzler 2003) FULL DVD.iso +: type: movie + format: DVD + title: Retour de Flammes + type: movie + year: 2003 + +? A.Common.Title.Special.2014.avi +: type: movie + year: 2014 + title: A Common Title Special + +? A.Common.Title.2014.Special.avi +: type: episode + year: 2014 + series: A Common Title + title: Special + special: Special + +? A.Common.Title.2014.Special.Edition.avi +: type: movie + year: 2014 + title: A Common Title + edition: Special Edition + +? Downton.Abbey.2013.Christmas.Special.HDTV.x264-FoV.mp4 +: type: episode + year: 2013 + series: Downton Abbey + title: Christmas Special + videoCodec: h264 + releaseGroup: FoV + format: HDTV + special: Special + +? Doctor_Who_2013_Christmas_Special.The_Time_of_The_Doctor.HD +: options: -n + type: episode + series: Doctor Who + other: HD + special: Special + title: Christmas Special The Time of The Doctor + year: 2013 + +? Doctor Who 2005 50th Anniversary Special The Day of the Doctor 3.avi +: type: episode + series: Doctor Who + special: Special + title: 50th Anniversary Special The Day of the Doctor 3 + year: 2005 + +? Robot Chicken S06-Born Again Virgin Christmas Special HDTV x264.avi +: type: episode + series: Robot Chicken + format: HDTV + season: 6 + title: Born Again Virgin Christmas Special + videoCodec: h264 + special: Special + +? Wicked.Tuna.S03E00.Head.To.Tail.Special.HDTV.x264-YesTV +: options: -n + type: episode + series: Wicked Tuna + title: Head To Tail Special + releaseGroup: YesTV + season: 3 + episodeNumber: 0 + videoCodec: h264 + format: HDTV + special: Special diff --git a/lib/guessit/test/dummy.srt b/lib/guessit/test/dummy.srt new file mode 100644 index 00000000..ca4cf8b8 --- /dev/null +++ b/lib/guessit/test/dummy.srt @@ -0,0 +1 @@ +Just a dummy srt file (used for unittests: do not remove!) diff --git a/lib/guessit/test/episodes.yaml b/lib/guessit/test/episodes.yaml new file mode 100644 index 00000000..31c0cae7 --- /dev/null +++ b/lib/guessit/test/episodes.yaml @@ -0,0 +1,569 @@ +# Dubious tests +# +#? "finale " +#: releaseGroup: FiNaLe +# extension: "" + + +? Series/Californication/Season 2/Californication.2x05.Vaginatown.HDTV.XviD-0TV.avi +: series: Californication + season: 2 + episodeNumber: 5 + title: Vaginatown + format: HDTV + videoCodec: XviD + releaseGroup: 0TV + +? Series/dexter/Dexter.5x02.Hello,.Bandit.ENG.-.sub.FR.HDTV.XviD-AlFleNi-TeaM.[tvu.org.ru].avi +: series: Dexter + season: 5 + episodeNumber: 2 + title: Hello, Bandit + language: English + subtitleLanguage: French + format: HDTV + videoCodec: XviD + releaseGroup: AlFleNi-TeaM + website: tvu.org.ru + +? Series/Treme/Treme.1x03.Right.Place,.Wrong.Time.HDTV.XviD-NoTV.avi +: series: Treme + season: 1 + episodeNumber: 3 + title: Right Place, Wrong Time + format: HDTV + videoCodec: XviD + releaseGroup: NoTV + +? Series/Duckman/Duckman - 101 (01) - 20021107 - I, Duckman.avi +: series: Duckman + season: 1 + episodeNumber: 1 + title: I, Duckman + date: 2002-11-07 + +? Series/Duckman/Duckman - S1E13 Joking The Chicken (unedited).avi +: series: Duckman + season: 1 + episodeNumber: 13 + title: Joking The Chicken + +? Series/Simpsons/Saison 12 Français/Simpsons,.The.12x08.A.Bas.Le.Sergent.Skinner.FR.avi +: series: The Simpsons + season: 12 + episodeNumber: 8 + title: A Bas Le Sergent Skinner + language: French + +? Series/Futurama/Season 3 (mkv)/[™] Futurama - S03E22 - Le chef de fer à 30% ( 30 Percent Iron Chef ).mkv +: series: Futurama + season: 3 + episodeNumber: 22 + title: Le chef de fer à 30% + +? Series/The Office/Season 6/The Office - S06xE01.avi +: series: The Office + season: 6 + episodeNumber: 1 + +? series/The Office/Season 4/The Office [401] Fun Run.avi +: series: The Office + season: 4 + episodeNumber: 1 + title: Fun Run + +? Series/Mad Men Season 1 Complete/Mad.Men.S01E01.avi +: series: Mad Men + season: 1 + episodeNumber: 1 + other: complete + +? series/Psych/Psych S02 Season 2 Complete English DVD/Psych.S02E02.65.Million.Years.Off.avi +: series: Psych + season: 2 + episodeNumber: 2 + title: 65 Million Years Off + language: english + format: DVD + other: complete + +? series/Psych/Psych S02 Season 2 Complete English DVD/Psych.S02E03.Psy.Vs.Psy.Français.srt +: series: Psych + season: 2 + episodeNumber: 3 + title: Psy Vs Psy + format: DVD + language: English + subtitleLanguage: French + other: complete + +? Series/Pure Laine/Pure.Laine.1x01.Toutes.Couleurs.Unies.FR.(Québec).DVB-Kceb.[tvu.org.ru].avi +: series: Pure Laine + season: 1 + episodeNumber: 1 + title: Toutes Couleurs Unies + format: DVB + releaseGroup: Kceb + language: french + website: tvu.org.ru + +? Series/Pure Laine/2x05 - Pure Laine - Je Me Souviens.avi +: series: Pure Laine + season: 2 + episodeNumber: 5 + title: Je Me Souviens + +? Series/Tout sur moi/Tout sur moi - S02E02 - Ménage à trois (14-01-2008) [Rip by Ampli].avi +: series: Tout sur moi + season: 2 + episodeNumber: 2 + title: Ménage à trois + date: 2008-01-14 + +? The.Mentalist.2x21.18-5-4.ENG.-.sub.FR.HDTV.XviD-AlFleNi-TeaM.[tvu.org.ru].avi +: series: The Mentalist + season: 2 + episodeNumber: 21 + title: 18-5-4 + language: english + subtitleLanguage: french + format: HDTV + videoCodec: Xvid + releaseGroup: AlFleNi-TeaM + website: tvu.org.ru + +? series/__ Incomplete __/Dr Slump (Catalan)/Dr._Slump_-_003_DVB-Rip_Catalan_by_kelf.avi +: series: Dr Slump + episodeNumber: 3 + format: DVB + language: catalan + +? series/Ren and Stimpy - Black_hole_[DivX].avi +: series: Ren and Stimpy + title: Black hole + videoCodec: DivX + +? Series/Walt Disney/Donald.Duck.-.Good.Scouts.[www.bigernie.jump.to].avi +: series: Donald Duck + title: Good Scouts + website: www.bigernie.jump.to + +? Series/Neverwhere/Neverwhere.05.Down.Street.[tvu.org.ru].avi +: series: Neverwhere + episodeNumber: 5 + title: Down Street + website: tvu.org.ru + +? Series/South Park/Season 4/South.Park.4x07.Cherokee.Hair.Tampons.DVDRip.[tvu.org.ru].avi +: series: South Park + season: 4 + episodeNumber: 7 + title: Cherokee Hair Tampons + format: DVD + website: tvu.org.ru + +? Series/Kaamelott/Kaamelott - Livre V - Ep 23 - Le Forfait.avi +: series: Kaamelott + episodeNumber: 23 + title: Le Forfait + +? Series/Duckman/Duckman - 110 (10) - 20021218 - Cellar Beware.avi +: series: Duckman + season: 1 + episodeNumber: 10 + date: 2002-12-18 + title: Cellar Beware + +? Series/Ren & Stimpy/Ren And Stimpy - Onward & Upward-Adult Party Cartoon.avi +: series: Ren And Stimpy + title: Onward & Upward-Adult Party Cartoon + +? Series/Breaking Bad/Minisodes/Breaking.Bad.(Minisodes).01.Good.Cop.Bad.Cop.WEBRip.XviD.avi +: series: Breaking Bad + episodeFormat: Minisode + episodeNumber: 1 + title: Good Cop Bad Cop + format: WEBRip + videoCodec: XviD + +? Series/My Name Is Earl/My.Name.Is.Earl.S01Extras.-.Bad.Karma.DVDRip.XviD.avi +: series: My Name Is Earl + season: 1 + title: Bad Karma + format: DVD + special: Extras + videoCodec: XviD + +? /mnt/series/The Big Bang Theory/S01/The.Big.Bang.Theory.S01E01.mkv +: series: The Big Bang Theory + season: 1 + episodeNumber: 1 + +? /media/Parks_and_Recreation-s03-e01.mkv +: series: Parks and Recreation + season: 3 + episodeNumber: 1 + +? /media/Parks_and_Recreation-s03-e02-Flu_Season.mkv +: series: Parks and Recreation + season: 3 + title: Flu Season + episodeNumber: 2 + +? /media/Parks_and_Recreation-s03-x01.mkv +: series: Parks and Recreation + season: 3 + bonusNumber: 1 + +? /media/Parks_and_Recreation-s03-x02-Gag_Reel.mkv +: series: Parks and Recreation + season: 3 + bonusNumber: 2 + bonusTitle: Gag Reel + +? /media/Band_of_Brothers-e01-Currahee.mkv +: series: Band of Brothers + episodeNumber: 1 + title: Currahee + +? /media/Band_of_Brothers-x02-We_Stand_Alone_Together.mkv +: series: Band of Brothers + bonusNumber: 2 + bonusTitle: We Stand Alone Together + +? /TV Shows/Mad.M-5x9.mkv +: series: Mad M + season: 5 + episodeNumber: 9 + +? /TV Shows/new.girl.117.hdtv-lol.mp4 +: series: New Girl + season: 1 + episodeNumber: 17 + format: HDTV + releaseGroup: LOL + +? Kaamelott - 5x44x45x46x47x48x49x50.avi +: series: Kaamelott + season: 5 + episodeNumber: 44 + episodeList: [44, 45, 46, 47, 48, 49, 50] + +? Example S01E01-02.avi +: series: Example + season: 1 + episodeNumber: 1 + episodeList: [1, 2] + +? Example S01E01E02.avi +: series: Example + season: 1 + episodeNumber: 1 + episodeList: [1, 2] + +? Series/Baccano!/Baccano!_-_T1_-_Trailer_-_[Ayu](dae8173e).mkv +: series: Baccano! + other: Trailer + +? Series/Doctor Who (2005)/Season 06/Doctor Who (2005) - S06E01 - The Impossible Astronaut (1).avi +: series: Doctor Who + year: 2005 + season: 6 + episodeNumber: 1 + title: The Impossible Astronaut + +? The.Office.(US).1x03.Health.Care.HDTV.XviD-LOL.avi +: series: The Office (US) + country: US + season: 1 + episodeNumber: 3 + title: Health Care + format: HDTV + videoCodec: XviD + releaseGroup: LOL + +? /Volumes/data-1/Series/Futurama/Season 3/Futurama_-_S03_DVD_Bonus_-_Deleted_Scenes_Part_3.ogm +: series: Futurama + season: 3 + other: Bonus + title: Deleted Scenes Part 3 + format: DVD + +? Ben.and.Kate.S01E02.720p.HDTV.X264-DIMENSION.mkv +: series: Ben and Kate + season: 1 + episodeNumber: 2 + screenSize: 720p + format: HDTV + videoCodec: h264 + releaseGroup: DIMENSION + +? /volume1/TV Series/Drawn Together/Season 1/Drawn Together 1x04 Requiem for a Reality Show.avi +: series: Drawn Together + season: 1 + episodeNumber: 4 + title: Requiem for a Reality Show + +? Sons.of.Anarchy.S05E06.720p.WEB.DL.DD5.1.H.264-CtrlHD.mkv +: series: Sons of Anarchy + season: 5 + episodeNumber: 6 + screenSize: 720p + format: WEB-DL + audioChannels: "5.1" + audioCodec: DolbyDigital + videoCodec: h264 + releaseGroup: CtrlHD + +? /media/bdc64bfe-e36f-4af8-b550-e6fd2dfaa507/TV_Shows/Doctor Who (2005)/Saison 6/Doctor Who (2005) - S06E13 - The Wedding of River Song.mkv +: series: Doctor Who + season: 6 + episodeNumber: 13 + year: 2005 + title: The Wedding of River Song + idNumber: bdc64bfe-e36f-4af8-b550-e6fd2dfaa507 + +? /mnt/videos/tvshows/Doctor Who/Season 06/E13 - The Wedding of River Song.mkv +: series: Doctor Who + season: 6 + episodeNumber: 13 + title: The Wedding of River Song + +? The.Simpsons.S24E03.Adventures.in.Baby-Getting.720p.WEB-DL.DD5.1.H.264-CtrlHD.mkv +: series: The Simpsons + season: 24 + episodeNumber: 3 + title: Adventures in Baby-Getting + screenSize: 720p + format: WEB-DL + audioChannels: "5.1" + audioCodec: DolbyDigital + videoCodec: h264 + releaseGroup: CtrlHD + +? /home/disaster/Videos/TV/Merlin/merlin_2008.5x02.arthurs_bane_part_two.repack.720p_hdtv_x264-fov.mkv +: series: Merlin + season: 5 + episodeNumber: 2 + title: Arthurs bane part two + screenSize: 720p + format: HDTV + videoCodec: h264 + releaseGroup: Fov + year: 2008 + other: Proper + +? "Da Vinci's Demons - 1x04 - The Magician.mkv" +: series: "Da Vinci's Demons" + season: 1 + episodeNumber: 4 + title: The Magician + +? CSI.S013E18.Sheltered.720p.WEB-DL.DD5.1.H.264.mkv +: series: CSI + season: 13 + episodeNumber: 18 + title: Sheltered + screenSize: 720p + format: WEB-DL + audioChannels: "5.1" + audioCodec: DolbyDigital + videoCodec: h264 + +? Game of Thrones S03E06 1080i HDTV DD5.1 MPEG2-TrollHD.ts +: series: Game of Thrones + season: 3 + episodeNumber: 6 + screenSize: 1080i + format: HDTV + audioChannels: "5.1" + audioCodec: DolbyDigital + videoCodec: MPEG2 + releaseGroup: TrollHD + +? gossip.girl.s01e18.hdtv.xvid-2hd.eng.srt +: series: gossip girl + season: 1 + episodeNumber: 18 + format: HDTV + videoCodec: XviD + releaseGroup: 2HD + subtitleLanguage: english + +? Wheels.S03E01E02.720p.HDTV.x264-IMMERSE.mkv +: series: Wheels + season: 3 + episodeNumber: 1 + episodeList: [1, 2] + screenSize: 720p + format: HDTV + videoCodec: h264 + releaseGroup: IMMERSE + +? Wheels.S03E01-02.720p.HDTV.x264-IMMERSE.mkv +: series: Wheels + season: 3 + episodeNumber: 1 + episodeList: [1, 2] + screenSize: 720p + format: HDTV + videoCodec: h264 + releaseGroup: IMMERSE + +? Wheels.S03E01-E02.720p.HDTV.x264-IMMERSE.mkv +: series: Wheels + season: 3 + episodeNumber: 1 + episodeList: [1, 2] + screenSize: 720p + format: HDTV + videoCodec: h264 + releaseGroup: IMMERSE + +? Wheels.S03E01-03.720p.HDTV.x264-IMMERSE.mkv +: series: Wheels + season: 3 + episodeNumber: 1 + episodeList: [1, 2, 3] + screenSize: 720p + format: HDTV + videoCodec: h264 + releaseGroup: IMMERSE + +? Marvels.Agents.of.S.H.I.E.L.D.S01E06.720p.HDTV.X264-DIMENSION.mkv +: series: Marvels Agents of S.H.I.E.L.D. + season: 1 + episodeNumber: 6 + screenSize: 720p + format: HDTV + videoCodec: h264 + releaseGroup: DIMENSION + +? Marvels.Agents.of.S.H.I.E.L.D..S01E06.720p.HDTV.X264-DIMENSION.mkv +: series: Marvels Agents of S.H.I.E.L.D. + season: 1 + episodeNumber: 6 + screenSize: 720p + format: HDTV + videoCodec: h264 + releaseGroup: DIMENSION + +? Series/Friday Night Lights/Season 1/Friday Night Lights S01E19 - Ch-Ch-Ch-Ch-Changes.avi +: series: Friday Night Lights + season: 1 + episodeNumber: 19 + title: Ch-Ch-Ch-Ch-Changes + +? Dexter Saison VII FRENCH.BDRip.XviD-MiND.nfo +: series: Dexter + season: 7 + videoCodec: XviD + language: French + format: BluRay + releaseGroup: MiND + +? Dexter Saison sept FRENCH.BDRip.XviD-MiND.nfo +: series: Dexter + season: 7 + videoCodec: XviD + language: French + format: BluRay + releaseGroup: MiND + +? "Pokémon S16 - E29 - 1280*720 HDTV VF.mkv" +: series: Pokémon + format: HDTV + language: French + season: 16 + episodeNumber: 29 + screenSize: 720p + +? One.Piece.E576.VOSTFR.720p.HDTV.x264-MARINE-FORD.mkv +: episodeNumber: 576 + videoCodec: h264 + format: HDTV + series: One Piece + releaseGroup: MARINE-FORD + subtitleLanguage: French + screenSize: 720p + +? Dexter.S08E12.FINAL.MULTi.1080p.BluRay.x264-MiND.mkv +: videoCodec: h264 + episodeNumber: 12 + season: 8 + format: BluRay + series: Dexter + other: final + language: Multiple languages + releaseGroup: MiND + screenSize: 1080p + +? One Piece - E623 VOSTFR HD [www.manga-ddl-free.com].mkv +: website: www.manga-ddl-free.com + episodeNumber: 623 + subtitleLanguage: French + series: One Piece + other: HD + +? Falling Skies Saison 1.HDLight.720p.x264.VFF.mkv +: language: French + screenSize: 720p + season: 1 + series: Falling Skies + videoCodec: h264 + +? Sleepy.Hollow.S01E09.720p.WEB-DL.DD5.1.H.264-BP.mkv +: episodeNumber: 9 + videoCodec: h264 + format: WEB-DL + series: Sleepy Hollow + audioChannels: "5.1" + screenSize: 720p + season: 1 + videoProfile: BP + audioCodec: DolbyDigital + +? Sleepy.Hollow.S01E09.720p.WEB-DL.DD5.1.H.264-BS.mkv +: episodeNumber: 9 + videoCodec: h264 + format: WEB-DL + series: Sleepy Hollow + audioChannels: "5.1" + screenSize: 720p + season: 1 + releaseGroup: BS + audioCodec: DolbyDigital + +? Battlestar.Galactica.S00.Pilot.FRENCH.DVDRip.XviD-NOTAG.avi +: series: Battlestar Galactica + season: 0 + title: Pilot + special: Pilot + language: French + format: DVD + videoCodec: XviD + releaseGroup: NOTAG + +? The Big Bang Theory S00E00 Unaired Pilot VOSTFR TVRip XviD-VioCs +: options: -n + series: The Big Bang Theory + season: 0 + episodeNumber: 0 + subtitleLanguage: French + format: TV + videoCodec: XviD + releaseGroup: VioCs + special: [Unaired, Pilot] + title: Unaired Pilot + +? The Big Bang Theory S01E00 PROPER Unaired Pilot TVRip XviD-GIGGITY +: options: -n + series: The Big Bang Theory + season: 1 + episodeNumber: 0 + format: TV + videoCodec: XviD + releaseGroup: GIGGITY + other: proper + special: [Unaired, Pilot] + title: Unaired Pilot diff --git a/lib/guessit/test/guessittest.py b/lib/guessit/test/guessittest.py new file mode 100644 index 00000000..9362ce68 --- /dev/null +++ b/lib/guessit/test/guessittest.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# GuessIt - A library for guessing information from filenames +# Copyright (c) 2013 Nicolas Wack +# +# GuessIt is free software; you can redistribute it and/or modify it under +# the terms of the Lesser GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GuessIt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Lesser GNU General Public License for more details. +# +# You should have received a copy of the Lesser GNU General Public License +# along with this program. If not, see . +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +from guessit import base_text_type, u + +from unittest import TestCase, TestLoader, TextTestRunner +import shlex + +import yaml, logging, sys, os +from os.path import * + + +def currentPath(): + '''Returns the path in which the calling file is located.''' + return dirname(join(os.getcwd(), sys._getframe(1).f_globals['__file__'])) + + +def addImportPath(path): + '''Function that adds the specified path to the import path. The path can be + absolute or relative to the calling file.''' + importPath = abspath(join(currentPath(), path)) + sys.path = [importPath] + sys.path + +log = logging.getLogger(__name__) + +from guessit.plugins import transformers +import guessit +from guessit.options import option_parser +from guessit import * +from guessit.matcher import * +from guessit.fileutils import * + + +def allTests(testClass): + return TestLoader().loadTestsFromTestCase(testClass) + + +class TestGuessit(TestCase): + + def checkMinimumFieldsCorrect(self, filename, filetype=None, remove_type=True, + exclude_files=None): + groundTruth = yaml.load(load_file_in_same_dir(__file__, filename)) + + def guess_func(string, options=None): + return guess_file_info(string, options=options, type=filetype) + + return self.checkFields(groundTruth, guess_func, remove_type, exclude_files) + + def checkFields(self, groundTruth, guess_func, remove_type=True, + exclude_files=None): + total = 0 + exclude_files = exclude_files or [] + + fails = {} + additionals = {} + + for filename, required_fields in groundTruth.items(): + filename = u(filename) + if filename in exclude_files: + continue + + log.debug('\n' + '-' * 120) + log.info('Guessing information for file: %s' % filename) + + options = required_fields.pop('options') if 'options' in required_fields else None + + if options: + args = shlex.split(options) + options, _ = option_parser.parse_args(args) + options = vars(options) + found = guess_func(filename, options) + + total = total + 1 + + # no need for these in the unittests + if remove_type: + try: + del found['type'] + except: + pass + for prop in ('container', 'mimetype'): + if prop in found: + del found[prop] + + # props which are list of just 1 elem should be opened for easier writing of the tests + for prop in ('language', 'subtitleLanguage', 'other', 'special'): + value = found.get(prop, None) + if isinstance(value, list) and len(value) == 1: + found[prop] = value[0] + + # look for missing properties + for prop, value in required_fields.items(): + if prop not in found: + log.debug("Prop '%s' not found in: %s" % (prop, filename)) + if not filename in fails: + fails[filename] = [] + fails[filename].append("'%s' not found in: %s" % (prop, filename)) + continue + + # if both properties are strings, do a case-insensitive comparison + if (isinstance(value, base_text_type) and + isinstance(found[prop], base_text_type)): + if value.lower() != found[prop].lower(): + log.debug("Wrong prop value [str] for '%s': expected = '%s' - received = '%s'" % (prop, u(value), u(found[prop]))) + if not filename in fails: + fails[filename] = [] + fails[filename].append("'%s': expected = '%s' - received = '%s'" % (prop, u(value), u(found[prop]))) + + # if both are lists, we assume list of strings and do a case-insensitive + # comparison on their elements + elif isinstance(value, list) and isinstance(found[prop], list): + s1 = set(u(s).lower() for s in value) + s2 = set(u(s).lower() for s in found[prop]) + if s1 != s2: + log.debug("Wrong prop value [list] for '%s': expected = '%s' - received = '%s'" % (prop, u(value), u(found[prop]))) + if not filename in fails: + fails[filename] = [] + fails[filename].append("'%s': expected = '%s' - received = '%s'" % (prop, u(value), u(found[prop]))) + # otherwise, just compare their values directly + else: + if found[prop] != value: + log.debug("Wrong prop value for '%s': expected = '%s' [%s] - received = '%s' [%s]" % (prop, u(value), type(value), u(found[prop]), type(found[prop]))) + if not filename in fails: + fails[filename] = [] + fails[filename].append("'%s': expected = '%s' [%s] - received = '%s' [%s]" % (prop, u(value), type(value), u(found[prop]), type(found[prop]))) + + # look for additional properties + for prop, value in found.items(): + if prop not in required_fields: + log.debug("Found additional info for prop = '%s': '%s'" % (prop, u(value))) + if not filename in additionals: + additionals[filename] = [] + additionals[filename].append("'%s': '%s'" % (prop, u(value))) + + correct = total - len(fails) + log.info('SUMMARY: Guessed correctly %d out of %d filenames' % (correct, total)) + + for failed_entry, failed_properties in fails.items(): + log.error('---- ' + failed_entry + ' ----') + for failed_property in failed_properties: + log.error("FAILED: " + failed_property) + + for additional_entry, additional_properties in additionals.items(): + log.warn('---- ' + additional_entry + ' ----') + for additional_property in additional_properties: + log.warn("ADDITIONAL: " + additional_property) + + self.assertTrue(correct == total, + msg='Correct: %d < Total: %d' % (correct, total)) diff --git a/lib/guessit/test/movies.yaml b/lib/guessit/test/movies.yaml new file mode 100644 index 00000000..5651d23d --- /dev/null +++ b/lib/guessit/test/movies.yaml @@ -0,0 +1,626 @@ + +? Movies/Fear and Loathing in Las Vegas (1998)/Fear.and.Loathing.in.Las.Vegas.720p.HDDVD.DTS.x264-ESiR.mkv +: title: Fear and Loathing in Las Vegas + year: 1998 + screenSize: 720p + format: HD-DVD + audioCodec: DTS + videoCodec: h264 + releaseGroup: ESiR + +? Movies/El Dia de la Bestia (1995)/El.dia.de.la.bestia.DVDrip.Spanish.DivX.by.Artik[SEDG].avi +: title: El Dia de la Bestia + year: 1995 + format: DVD + language: spanish + videoCodec: DivX + +? Movies/Dark City (1998)/Dark.City.(1998).DC.BDRip.720p.DTS.X264-CHD.mkv +: title: Dark City + year: 1998 + format: BluRay + screenSize: 720p + audioCodec: DTS + videoCodec: h264 + releaseGroup: CHD + +? Movies/Sin City (BluRay) (2005)/Sin.City.2005.BDRip.720p.x264.AC3-SEPTiC.mkv +: title: Sin City + year: 2005 + format: BluRay + screenSize: 720p + videoCodec: h264 + audioCodec: AC3 + releaseGroup: SEPTiC + + +? Movies/Borat (2006)/Borat.(2006).R5.PROPER.REPACK.DVDRip.XviD-PUKKA.avi +: title: Borat + year: 2006 + other: PROPER + format: DVD + other: [ R5, Proper ] + videoCodec: XviD + releaseGroup: PUKKA + + +? "[XCT].Le.Prestige.(The.Prestige).DVDRip.[x264.HP.He-Aac.{Fr-Eng}.St{Fr-Eng}.Chaps].mkv" +: title: Le Prestige + format: DVD + videoCodec: h264 + videoProfile: HP + audioCodec: AAC + audioProfile: HE + language: [ french, english ] + subtitleLanguage: [ french, english ] + +? Battle Royale (2000)/Battle.Royale.(Batoru.Rowaiaru).(2000).(Special.Edition).CD1of2.DVDRiP.XviD-[ZeaL].avi +: title: Battle Royale + year: 2000 + edition: special edition + cdNumber: 1 + cdNumberTotal: 2 + format: DVD + videoCodec: XviD + releaseGroup: ZeaL + +? Movies/Brazil (1985)/Brazil_Criterion_Edition_(1985).CD2.avi +: title: Brazil + edition: Criterion Edition + year: 1985 + cdNumber: 2 + +? Movies/Persepolis (2007)/[XCT] Persepolis [H264+Aac-128(Fr-Eng)+ST(Fr-Eng)+Ind].mkv +: title: Persepolis + year: 2007 + videoCodec: h264 + audioCodec: AAC + language: [ French, English ] + subtitleLanguage: [ French, English ] + +? Movies/Toy Story (1995)/Toy Story [HDTV 720p English-Spanish].mkv +: title: Toy Story + year: 1995 + format: HDTV + screenSize: 720p + language: [ english, spanish ] + +? Movies/Office Space (1999)/Office.Space.[Dual-DVDRip].[Spanish-English].[XviD-AC3-AC3].[by.Oswald].avi +: title: Office Space + year: 1999 + format: DVD + language: [ english, spanish ] + videoCodec: XviD + audioCodec: AC3 + +? Movies/Wild Zero (2000)/Wild.Zero.DVDivX-EPiC.avi +: title: Wild Zero + year: 2000 + videoCodec: DivX + releaseGroup: EPiC + +? movies/Baraka_Edition_Collector.avi +: title: Baraka + edition: collector edition + +? Movies/Blade Runner (1982)/Blade.Runner.(1982).(Director's.Cut).CD1.DVDRip.XviD.AC3-WAF.avi +: title: Blade Runner + year: 1982 + edition: Director's Cut + cdNumber: 1 + format: DVD + videoCodec: XviD + audioCodec: AC3 + releaseGroup: WAF + +? movies/American.The.Bill.Hicks.Story.2009.DVDRip.XviD-EPiSODE.[UsaBit.com]/UsaBit.com_esd-americanbh.avi +: title: American The Bill Hicks Story + year: 2009 + format: DVD + videoCodec: XviD + releaseGroup: EPiSODE + website: UsaBit.com + +? movies/Charlie.And.Boots.DVDRip.XviD-TheWretched/wthd-cab.avi +: title: Charlie And Boots + format: DVD + videoCodec: XviD + releaseGroup: TheWretched + +? movies/Steig Larsson Millenium Trilogy (2009) BRrip 720 AAC x264/(1)The Girl With The Dragon Tattoo (2009) BRrip 720 AAC x264.mkv +: title: The Girl With The Dragon Tattoo + filmSeries: Steig Larsson Millenium Trilogy + filmNumber: 1 + year: 2009 + format: BluRay + audioCodec: AAC + videoCodec: h264 + screenSize: 720p + +? movies/Greenberg.REPACK.LiMiTED.DVDRip.XviD-ARROW/arw-repack-greenberg.dvdrip.xvid.avi +: title: Greenberg + format: DVD + videoCodec: XviD + releaseGroup: ARROW + other: ['Proper', 'Limited'] + +? Movies/Fr - Paris 2054, Renaissance (2005) - De Christian Volckman - (Film Divx Science Fiction Fantastique Thriller Policier N&B).avi +: title: Paris 2054, Renaissance + year: 2005 + language: french + videoCodec: DivX + +? Movies/[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi +: title: Avida + year: 2006 + language: french + format: DVD + videoCodec: XviD + releaseGroup: PROD + +? Movies/Alice in Wonderland DVDRip.XviD-DiAMOND/dmd-aw.avi +: title: Alice in Wonderland + format: DVD + videoCodec: XviD + releaseGroup: DiAMOND + +? Movies/Ne.Le.Dis.A.Personne.Fr 2 cd/personnea_mp.avi +: title: Ne Le Dis A Personne + language: french + cdNumberTotal: 2 + +? Movies/Bunker Palace Hôtel (Enki Bilal) (1989)/Enki Bilal - Bunker Palace Hotel (Fr Vhs Rip).avi +: title: Bunker Palace Hôtel + year: 1989 + language: french + format: VHS + +? Movies/21 (2008)/21.(2008).DVDRip.x264.AC3-FtS.[sharethefiles.com].mkv +: title: "21" + year: 2008 + format: DVD + videoCodec: h264 + audioCodec: AC3 + releaseGroup: FtS + website: sharethefiles.com + +? Movies/9 (2009)/9.2009.Blu-ray.DTS.720p.x264.HDBRiSe.[sharethefiles.com].mkv +: title: "9" + year: 2009 + format: BluRay + audioCodec: DTS + screenSize: 720p + videoCodec: h264 + releaseGroup: HDBRiSe + website: sharethefiles.com + +? Movies/Mamma.Mia.2008.DVDRip.AC3.XviD-CrazyTeam/Mamma.Mia.2008.DVDRip.AC3.XviD-CrazyTeam.avi +: title: Mamma Mia + year: 2008 + format: DVD + audioCodec: AC3 + videoCodec: XviD + releaseGroup: CrazyTeam + +? Movies/M.A.S.H. (1970)/MASH.(1970).[Divx.5.02][Dual-Subtitulos][DVDRip].ogm +: title: M.A.S.H. + year: 1970 + videoCodec: DivX + format: DVD + +? Movies/The Doors (1991)/09.03.08.The.Doors.(1991).BDRip.720p.AC3.X264-HiS@SiLUHD-English.[sharethefiles.com].mkv +: title: The Doors + year: 1991 + date: 2008-03-09 + format: BluRay + screenSize: 720p + audioCodec: AC3 + videoCodec: h264 + releaseGroup: HiS@SiLUHD + language: english + website: sharethefiles.com + +? Movies/Ratatouille/video_ts-ratatouille.srt +: title: Ratatouille + format: DVD + +? Movies/001 __ A classer/Fantomas se déchaine - Louis de Funès.avi +: title: Fantomas se déchaine + +? Movies/Comme une Image (2004)/Comme.Une.Image.FRENCH.DVDRiP.XViD-NTK.par-www.divx-overnet.com.avi +: title: Comme une Image + year: 2004 + language: french + format: DVD + videoCodec: XviD + releaseGroup: NTK + website: www.divx-overnet.com + +? Movies/Fantastic Mr Fox/Fantastic.Mr.Fox.2009.DVDRip.{x264+LC-AAC.5.1}{Fr-Eng}{Sub.Fr-Eng}-™.[sharethefiles.com].mkv +: title: Fantastic Mr Fox + year: 2009 + format: DVD + videoCodec: h264 + audioCodec: AAC + audioProfile: LC + audioChannels: "5.1" + language: [ french, english ] + subtitleLanguage: [ french, english ] + website: sharethefiles.com + +? Movies/Somewhere.2010.DVDRip.XviD-iLG/i-smwhr.avi +: title: Somewhere + year: 2010 + format: DVD + videoCodec: XviD + releaseGroup: iLG + +? Movies/Moon_(2009).mkv +: title: Moon + year: 2009 + +? Movies/Moon_(2009)-x01.mkv +: title: Moon + year: 2009 + bonusNumber: 1 + +? Movies/Moon_(2009)-x02-Making_Of.mkv +: title: Moon + year: 2009 + bonusNumber: 2 + bonusTitle: Making Of + +? movies/James_Bond-f17-Goldeneye.mkv +: title: Goldeneye + filmSeries: James Bond + filmNumber: 17 + +? /movies/James_Bond-f21-Casino_Royale.mkv +: title: Casino Royale + filmSeries: James Bond + filmNumber: 21 + +? /movies/James_Bond-f21-Casino_Royale-x01-Becoming_Bond.mkv +: title: Casino Royale + filmSeries: James Bond + filmNumber: 21 + bonusNumber: 1 + bonusTitle: Becoming Bond + +? /movies/James_Bond-f21-Casino_Royale-x02-Stunts.mkv +: title: Casino Royale + filmSeries: James Bond + filmNumber: 21 + bonusNumber: 2 + bonusTitle: Stunts + +? OSS_117--Cairo,_Nest_of_Spies.mkv +: title: OSS 117--Cairo, Nest of Spies + +? The Godfather Part III.mkv +: title: The Godfather Part III + +? Foobar Part VI.mkv +: title: Foobar Part VI + +? The_Insider-(1999)-x02-60_Minutes_Interview-1996.mp4 +: title: The Insider + year: 1999 + bonusNumber: 2 + bonusTitle: 60 Minutes Interview-1996 + +? Rush.._Beyond_The_Lighted_Stage-x09-Between_Sun_and_Moon-2002_Hartford.mkv +: title: Rush Beyond The Lighted Stage + bonusNumber: 9 + bonusTitle: Between Sun and Moon-2002 Hartford + +? /public/uTorrent/Downloads Finished/Movies/Indiana.Jones.and.the.Temple.of.Doom.1984.HDTV.720p.x264.AC3.5.1-REDµX/Indiana.Jones.and.the.Temple.of.Doom.1984.HDTV.720p.x264.AC3.5.1-REDµX.mkv +: title: Indiana Jones and the Temple of Doom + year: 1984 + format: HDTV + screenSize: 720p + videoCodec: h264 + audioCodec: AC3 + audioChannels: "5.1" + releaseGroup: REDµX + +? The.Director’s.Notebook.2006.Blu-Ray.x264.DXVA.720p.AC3-de[42].mkv +: title: The Director’s Notebook + year: 2006 + format: BluRay + videoCodec: h264 + videoApi: DXVA + screenSize: 720p + audioCodec: AC3 + releaseGroup: de[42] + +? Movies/Cosmopolis.2012.LiMiTED.720p.BluRay.x264-AN0NYM0US[bb]/ano-cosmo.720p.mkv +: title: Cosmopolis + year: 2012 + screenSize: 720p + videoCodec: h264 + releaseGroup: AN0NYM0US[bb] + format: BluRay + other: LIMITED + +? movies/La Science des Rêves (2006)/La.Science.Des.Reves.FRENCH.DVDRip.XviD-MP-AceBot.avi +: title: La Science des Rêves + year: 2006 + format: DVD + videoCodec: XviD + videoProfile: MP + releaseGroup: AceBot + language: French + +? The_Italian_Job.mkv +: title: The Italian Job + +? The.Rum.Diary.2011.1080p.BluRay.DTS.x264.D-Z0N3.mkv +: title: The Rum Diary + year: 2011 + screenSize: 1080p + format: BluRay + videoCodec: h264 + audioCodec: DTS + releaseGroup: D-Z0N3 + +? Life.Of.Pi.2012.1080p.BluRay.DTS.x264.D-Z0N3.mkv +: title: Life Of Pi + year: 2012 + screenSize: 1080p + format: BluRay + videoCodec: h264 + audioCodec: DTS + releaseGroup: D-Z0N3 + +? The.Kings.Speech.2010.1080p.BluRay.DTS.x264.D Z0N3.mkv +: title: The Kings Speech + year: 2010 + screenSize: 1080p + format: BluRay + audioCodec: DTS + videoCodec: h264 + releaseGroup: D-Z0N3 + +? Street.Kings.2008.BluRay.1080p.DTS.x264.dxva EuReKA.mkv +: title: Street Kings + year: 2008 + format: BluRay + screenSize: 1080p + audioCodec: DTS + videoCodec: h264 + videoApi: DXVA + releaseGroup: EuReKa + +? 2001.A.Space.Odyssey.1968.HDDVD.1080p.DTS.x264.dxva EuReKA.mkv +: title: 2001 A Space Odyssey + year: 1968 + format: HD-DVD + screenSize: 1080p + audioCodec: DTS + videoCodec: h264 + videoApi: DXVA + releaseGroup: EuReKa + +? 2012.2009.720p.BluRay.x264.DTS WiKi.mkv +: title: "2012" + year: 2009 + screenSize: 720p + format: BluRay + videoCodec: h264 + audioCodec: DTS + releaseGroup: WiKi + +? /share/Download/movie/Dead Man Down (2013) BRRiP XViD DD5_1 Custom NLSubs =-_lt Q_o_Q gt-=_/XD607ebb-BRc59935-5155473f-1c5f49/XD607ebb-BRc59935-5155473f-1c5f49.avi +: title: Dead Man Down + year: 2013 + format: BluRay + videoCodec: XviD + audioChannels: "5.1" + audioCodec: DolbyDigital + idNumber: XD607ebb-BRc59935-5155473f-1c5f49 + +? Pacific.Rim.3D.2013.COMPLETE.BLURAY-PCH.avi +: title: Pacific Rim + year: 2013 + format: BluRay + other: + - complete + - 3D + releaseGroup: PCH + +? Immersion.French.2011.STV.READNFO.QC.FRENCH.ENGLISH.NTSC.DVDR.nfo +: title: Immersion French + year: 2011 + language: + - French + - English + +? Immersion.French.2011.STV.READNFO.QC.FRENCH.NTSC.DVDR.nfo +: title: Immersion French + year: 2011 + language: French + +? Immersion.French.2011.STV.READNFO.QC.NTSC.DVDR.nfo +: title: Immersion French + year: 2011 + +? French.Immersion.2011.STV.READNFO.QC.ENGLISH.NTSC.DVDR.nfo +: title: French Immersion + year: 2011 + language: ENGLISH + +? Howl's_Moving_Castle_(2004)_[720p,HDTV,x264,DTS]-FlexGet.avi +: videoCodec: h264 + format: HDTV + title: Howl's Moving Castle + screenSize: 720p + year: 2004 + audioCodec: DTS + releaseGroup: FlexGet + +? Pirates de langkasuka.2008.FRENCH.1920X1080.h264.AVC.AsiaRa.mkv +: screenSize: 1080p + year: 2008 + language: French + videoCodec: h264 + title: Pirates de langkasuka + releaseGroup: AsiaRa + +? Masala (2013) Telugu Movie HD DVDScr XviD - Exclusive.avi +: year: 2013 + videoCodec: XviD + title: Masala + format: HD-DVD + other: screener + language: Telugu + releaseGroup: Exclusive + +? Django Unchained 2012 DVDSCR X264 AAC-P2P.nfo +: year: 2012 + other: screener + videoCodec: h264 + title: Django Unchained + audioCodec: AAC + format: DVD + releaseGroup: P2P + +? Ejecutiva.En.Apuros(2009).BLURAY.SCR.Xvid.Spanish.LanzamientosD.nfo +: year: 2009 + other: screener + format: BluRay + videoCodec: XviD + language: Spanish + title: Ejecutiva En Apuros + +? Die.Schluempfe.2.German.DL.1080p.BluRay.x264-EXQUiSiTE.mkv +: title: Die Schluempfe 2 + format: BluRay + language: + - Multiple languages + - German + videoCodec: h264 + releaseGroup: EXQUiSiTE + screenSize: 1080p + +? Rocky 1976 French SubForced BRRip x264 AC3-FUNKY.mkv +: title: Rocky + year: 1976 + subtitleLanguage: French + format: BluRay + videoCodec: h264 + audioCodec: AC3 + releaseGroup: FUNKY + +? REDLINE (BD 1080p H264 10bit FLAC) [3xR].mkv +: title: REDLINE + format: BluRay + videoCodec: h264 + videoProfile: 10bit + audioCodec: Flac + screenSize: 1080p + +? The.Lizzie.McGuire.Movie.(2003).HR.DVDRiP.avi +: title: The Lizzie McGuire Movie + year: 2003 + screenSize: 480p + format: DVD + +? Hua.Mulan.BRRIP.MP4.x264.720p-HR.avi +: title: Hua Mulan + videoCodec: h264 + format: BluRay + screenSize: 720p + +? Dr.Seuss.The.Lorax.2012.DVDRip.LiNE.XviD.AC3.HQ.Hive-CM8.mp4 +: videoCodec: XviD + title: Dr Seuss The Lorax + format: DVD + other: LiNE + year: 2012 + audioCodec: AC3 + audioProfile: HQ + releaseGroup: Hive-CM8 + + +? "Star Wars: Episode IV - A New Hope (2004) Special Edition.MKV" +: title: Star Wars Episode IV + year: 2004 + edition: Special Edition + +? Dr.LiNE.The.Lorax.2012.DVDRip.LiNE.XviD.AC3.HQ.Hive-CM8.mp4 +: videoCodec: XviD + title: Dr LiNE The Lorax + format: DVD + other: LiNE + year: 2012 + audioCodec: AC3 + audioProfile: HQ + releaseGroup: Hive-CM8 + +? Perfect Child-2007-TRUEFRENCH-TVRip.Xvid-h@mster.avi +: releaseGroup: h@mster + title: Perfect Child + videoCodec: XviD + language: French + format: TV + year: 2007 + +? entre.ciel.et.terre.(1994).dvdrip.h264.aac-psypeon.avi +: audioCodec: AAC + format: DVD + releaseGroup: psypeon + title: entre ciel et terre + videoCodec: h264 + year: 1994 + +? Yves.Saint.Laurent.2013.FRENCH.DVDSCR.MD.XviD-ViVARiUM.avi +: format: DVD + language: French + other: Screener + releaseGroup: ViVARiUM + title: Yves Saint Laurent + videoCodec: XviD + year: 2013 + +? Echec et Mort - Hard to Kill - Steven Seagal Multi 1080p BluRay x264 CCATS.avi +: format: BluRay + language: Multiple languages + releaseGroup: CCATS + screenSize: 1080p + title: Echec et Mort + videoCodec: h264 + +? Paparazzi - Timsit/Lindon (MKV 1080p tvripHD) +: options: -n + title: Paparazzi + screenSize: 1080p + format: HDTV + +? some.movie.720p.bluray.x264-mind +: options: -n + title: some movie + screenSize: 720p + videoCodec: h264 + releaseGroup: mind + format: BluRay + +? Dr LiNE The Lorax 720p h264 BluRay +: options: -n + title: Dr LiNE The Lorax + screenSize: 720p + videoCodec: h264 + format: BluRay + +? BeatdownFrenchDVDRip.mkv +: title: Beatdown + language: French + format: DVD + +? YvesSaintLaurent2013FrenchDVDScrXvid.avi +: format: DVD + language: French + other: Screener + title: Yves saint laurent + videoCodec: XviD + year: 2013 \ No newline at end of file diff --git a/lib/guessit/test/opensubtitles_languages_2012_05_09.txt b/lib/guessit/test/opensubtitles_languages_2012_05_09.txt new file mode 100644 index 00000000..4a08d9b5 --- /dev/null +++ b/lib/guessit/test/opensubtitles_languages_2012_05_09.txt @@ -0,0 +1,473 @@ +IdSubLanguage ISO639 LanguageName UploadEnabled WebEnabled +aar aa Afar, afar 0 0 +abk ab Abkhazian 0 0 +ace Achinese 0 0 +ach Acoli 0 0 +ada Adangme 0 0 +ady adyghé 0 0 +afa Afro-Asiatic (Other) 0 0 +afh Afrihili 0 0 +afr af Afrikaans 0 0 +ain Ainu 0 0 +aka ak Akan 0 0 +akk Akkadian 0 0 +alb sq Albanian 1 1 +ale Aleut 0 0 +alg Algonquian languages 0 0 +alt Southern Altai 0 0 +amh am Amharic 0 0 +ang English, Old (ca.450-1100) 0 0 +apa Apache languages 0 0 +ara ar Arabic 1 1 +arc Aramaic 0 0 +arg an Aragonese 0 0 +arm hy Armenian 1 0 +arn Araucanian 0 0 +arp Arapaho 0 0 +art Artificial (Other) 0 0 +arw Arawak 0 0 +asm as Assamese 0 0 +ast Asturian, Bable 0 0 +ath Athapascan languages 0 0 +aus Australian languages 0 0 +ava av Avaric 0 0 +ave ae Avestan 0 0 +awa Awadhi 0 0 +aym ay Aymara 0 0 +aze az Azerbaijani 0 0 +bad Banda 0 0 +bai Bamileke languages 0 0 +bak ba Bashkir 0 0 +bal Baluchi 0 0 +bam bm Bambara 0 0 +ban Balinese 0 0 +baq eu Basque 1 1 +bas Basa 0 0 +bat Baltic (Other) 0 0 +bej Beja 0 0 +bel be Belarusian 0 0 +bem Bemba 0 0 +ben bn Bengali 1 0 +ber Berber (Other) 0 0 +bho Bhojpuri 0 0 +bih bh Bihari 0 0 +bik Bikol 0 0 +bin Bini 0 0 +bis bi Bislama 0 0 +bla Siksika 0 0 +bnt Bantu (Other) 0 0 +bos bs Bosnian 1 0 +bra Braj 0 0 +bre br Breton 1 0 +btk Batak (Indonesia) 0 0 +bua Buriat 0 0 +bug Buginese 0 0 +bul bg Bulgarian 1 1 +bur my Burmese 0 0 +byn Blin 0 0 +cad Caddo 0 0 +cai Central American Indian (Other) 0 0 +car Carib 0 0 +cat ca Catalan 1 1 +cau Caucasian (Other) 0 0 +ceb Cebuano 0 0 +cel Celtic (Other) 0 0 +cha ch Chamorro 0 0 +chb Chibcha 0 0 +che ce Chechen 0 0 +chg Chagatai 0 0 +chi zh Chinese 1 1 +chk Chuukese 0 0 +chm Mari 0 0 +chn Chinook jargon 0 0 +cho Choctaw 0 0 +chp Chipewyan 0 0 +chr Cherokee 0 0 +chu cu Church Slavic 0 0 +chv cv Chuvash 0 0 +chy Cheyenne 0 0 +cmc Chamic languages 0 0 +cop Coptic 0 0 +cor kw Cornish 0 0 +cos co Corsican 0 0 +cpe Creoles and pidgins, English based (Other) 0 0 +cpf Creoles and pidgins, French-based (Other) 0 0 +cpp Creoles and pidgins, Portuguese-based (Other) 0 0 +cre cr Cree 0 0 +crh Crimean Tatar 0 0 +crp Creoles and pidgins (Other) 0 0 +csb Kashubian 0 0 +cus Cushitic (Other)' couchitiques, autres langues 0 0 +cze cs Czech 1 1 +dak Dakota 0 0 +dan da Danish 1 1 +dar Dargwa 0 0 +day Dayak 0 0 +del Delaware 0 0 +den Slave (Athapascan) 0 0 +dgr Dogrib 0 0 +din Dinka 0 0 +div dv Divehi 0 0 +doi Dogri 0 0 +dra Dravidian (Other) 0 0 +dua Duala 0 0 +dum Dutch, Middle (ca.1050-1350) 0 0 +dut nl Dutch 1 1 +dyu Dyula 0 0 +dzo dz Dzongkha 0 0 +efi Efik 0 0 +egy Egyptian (Ancient) 0 0 +eka Ekajuk 0 0 +elx Elamite 0 0 +eng en English 1 1 +enm English, Middle (1100-1500) 0 0 +epo eo Esperanto 1 0 +est et Estonian 1 1 +ewe ee Ewe 0 0 +ewo Ewondo 0 0 +fan Fang 0 0 +fao fo Faroese 0 0 +fat Fanti 0 0 +fij fj Fijian 0 0 +fil Filipino 0 0 +fin fi Finnish 1 1 +fiu Finno-Ugrian (Other) 0 0 +fon Fon 0 0 +fre fr French 1 1 +frm French, Middle (ca.1400-1600) 0 0 +fro French, Old (842-ca.1400) 0 0 +fry fy Frisian 0 0 +ful ff Fulah 0 0 +fur Friulian 0 0 +gaa Ga 0 0 +gay Gayo 0 0 +gba Gbaya 0 0 +gem Germanic (Other) 0 0 +geo ka Georgian 1 1 +ger de German 1 1 +gez Geez 0 0 +gil Gilbertese 0 0 +gla gd Gaelic 0 0 +gle ga Irish 0 0 +glg gl Galician 1 1 +glv gv Manx 0 0 +gmh German, Middle High (ca.1050-1500) 0 0 +goh German, Old High (ca.750-1050) 0 0 +gon Gondi 0 0 +gor Gorontalo 0 0 +got Gothic 0 0 +grb Grebo 0 0 +grc Greek, Ancient (to 1453) 0 0 +ell el Greek 1 1 +grn gn Guarani 0 0 +guj gu Gujarati 0 0 +gwi Gwich´in 0 0 +hai Haida 0 0 +hat ht Haitian 0 0 +hau ha Hausa 0 0 +haw Hawaiian 0 0 +heb he Hebrew 1 1 +her hz Herero 0 0 +hil Hiligaynon 0 0 +him Himachali 0 0 +hin hi Hindi 1 1 +hit Hittite 0 0 +hmn Hmong 0 0 +hmo ho Hiri Motu 0 0 +hrv hr Croatian 1 1 +hun hu Hungarian 1 1 +hup Hupa 0 0 +iba Iban 0 0 +ibo ig Igbo 0 0 +ice is Icelandic 1 1 +ido io Ido 0 0 +iii ii Sichuan Yi 0 0 +ijo Ijo 0 0 +iku iu Inuktitut 0 0 +ile ie Interlingue 0 0 +ilo Iloko 0 0 +ina ia Interlingua (International Auxiliary Language Asso 0 0 +inc Indic (Other) 0 0 +ind id Indonesian 1 1 +ine Indo-European (Other) 0 0 +inh Ingush 0 0 +ipk ik Inupiaq 0 0 +ira Iranian (Other) 0 0 +iro Iroquoian languages 0 0 +ita it Italian 1 1 +jav jv Javanese 0 0 +jpn ja Japanese 1 1 +jpr Judeo-Persian 0 0 +jrb Judeo-Arabic 0 0 +kaa Kara-Kalpak 0 0 +kab Kabyle 0 0 +kac Kachin 0 0 +kal kl Kalaallisut 0 0 +kam Kamba 0 0 +kan kn Kannada 0 0 +kar Karen 0 0 +kas ks Kashmiri 0 0 +kau kr Kanuri 0 0 +kaw Kawi 0 0 +kaz kk Kazakh 1 0 +kbd Kabardian 0 0 +kha Khasi 0 0 +khi Khoisan (Other) 0 0 +khm km Khmer 1 1 +kho Khotanese 0 0 +kik ki Kikuyu 0 0 +kin rw Kinyarwanda 0 0 +kir ky Kirghiz 0 0 +kmb Kimbundu 0 0 +kok Konkani 0 0 +kom kv Komi 0 0 +kon kg Kongo 0 0 +kor ko Korean 1 1 +kos Kosraean 0 0 +kpe Kpelle 0 0 +krc Karachay-Balkar 0 0 +kro Kru 0 0 +kru Kurukh 0 0 +kua kj Kuanyama 0 0 +kum Kumyk 0 0 +kur ku Kurdish 0 0 +kut Kutenai 0 0 +lad Ladino 0 0 +lah Lahnda 0 0 +lam Lamba 0 0 +lao lo Lao 0 0 +lat la Latin 0 0 +lav lv Latvian 1 0 +lez Lezghian 0 0 +lim li Limburgan 0 0 +lin ln Lingala 0 0 +lit lt Lithuanian 1 0 +lol Mongo 0 0 +loz Lozi 0 0 +ltz lb Luxembourgish 1 0 +lua Luba-Lulua 0 0 +lub lu Luba-Katanga 0 0 +lug lg Ganda 0 0 +lui Luiseno 0 0 +lun Lunda 0 0 +luo Luo (Kenya and Tanzania) 0 0 +lus lushai 0 0 +mac mk Macedonian 1 1 +mad Madurese 0 0 +mag Magahi 0 0 +mah mh Marshallese 0 0 +mai Maithili 0 0 +mak Makasar 0 0 +mal ml Malayalam 0 0 +man Mandingo 0 0 +mao mi Maori 0 0 +map Austronesian (Other) 0 0 +mar mr Marathi 0 0 +mas Masai 0 0 +may ms Malay 1 1 +mdf Moksha 0 0 +mdr Mandar 0 0 +men Mende 0 0 +mga Irish, Middle (900-1200) 0 0 +mic Mi'kmaq 0 0 +min Minangkabau 0 0 +mis Miscellaneous languages 0 0 +mkh Mon-Khmer (Other) 0 0 +mlg mg Malagasy 0 0 +mlt mt Maltese 0 0 +mnc Manchu 0 0 +mni Manipuri 0 0 +mno Manobo languages 0 0 +moh Mohawk 0 0 +mol mo Moldavian 0 0 +mon mn Mongolian 1 0 +mos Mossi 0 0 +mwl Mirandese 0 0 +mul Multiple languages 0 0 +mun Munda languages 0 0 +mus Creek 0 0 +mwr Marwari 0 0 +myn Mayan languages 0 0 +myv Erzya 0 0 +nah Nahuatl 0 0 +nai North American Indian 0 0 +nap Neapolitan 0 0 +nau na Nauru 0 0 +nav nv Navajo 0 0 +nbl nr Ndebele, South 0 0 +nde nd Ndebele, North 0 0 +ndo ng Ndonga 0 0 +nds Low German 0 0 +nep ne Nepali 0 0 +new Nepal Bhasa 0 0 +nia Nias 0 0 +nic Niger-Kordofanian (Other) 0 0 +niu Niuean 0 0 +nno nn Norwegian Nynorsk 0 0 +nob nb Norwegian Bokmal 0 0 +nog Nogai 0 0 +non Norse, Old 0 0 +nor no Norwegian 1 1 +nso Northern Sotho 0 0 +nub Nubian languages 0 0 +nwc Classical Newari 0 0 +nya ny Chichewa 0 0 +nym Nyamwezi 0 0 +nyn Nyankole 0 0 +nyo Nyoro 0 0 +nzi Nzima 0 0 +oci oc Occitan 1 1 +oji oj Ojibwa 0 0 +ori or Oriya 0 0 +orm om Oromo 0 0 +osa Osage 0 0 +oss os Ossetian 0 0 +ota Turkish, Ottoman (1500-1928) 0 0 +oto Otomian languages 0 0 +paa Papuan (Other) 0 0 +pag Pangasinan 0 0 +pal Pahlavi 0 0 +pam Pampanga 0 0 +pan pa Panjabi 0 0 +pap Papiamento 0 0 +pau Palauan 0 0 +peo Persian, Old (ca.600-400 B.C.) 0 0 +per fa Persian 1 1 +phi Philippine (Other) 0 0 +phn Phoenician 0 0 +pli pi Pali 0 0 +pol pl Polish 1 1 +pon Pohnpeian 0 0 +por pt Portuguese 1 1 +pra Prakrit languages 0 0 +pro Provençal, Old (to 1500) 0 0 +pus ps Pushto 0 0 +que qu Quechua 0 0 +raj Rajasthani 0 0 +rap Rapanui 0 0 +rar Rarotongan 0 0 +roa Romance (Other) 0 0 +roh rm Raeto-Romance 0 0 +rom Romany 0 0 +run rn Rundi 0 0 +rup Aromanian 0 0 +rus ru Russian 1 1 +sad Sandawe 0 0 +sag sg Sango 0 0 +sah Yakut 0 0 +sai South American Indian (Other) 0 0 +sal Salishan languages 0 0 +sam Samaritan Aramaic 0 0 +san sa Sanskrit 0 0 +sas Sasak 0 0 +sat Santali 0 0 +scc sr Serbian 1 1 +scn Sicilian 0 0 +sco Scots 0 0 +sel Selkup 0 0 +sem Semitic (Other) 0 0 +sga Irish, Old (to 900) 0 0 +sgn Sign Languages 0 0 +shn Shan 0 0 +sid Sidamo 0 0 +sin si Sinhalese 1 1 +sio Siouan languages 0 0 +sit Sino-Tibetan (Other) 0 0 +sla Slavic (Other) 0 0 +slo sk Slovak 1 1 +slv sl Slovenian 1 1 +sma Southern Sami 0 0 +sme se Northern Sami 0 0 +smi Sami languages (Other) 0 0 +smj Lule Sami 0 0 +smn Inari Sami 0 0 +smo sm Samoan 0 0 +sms Skolt Sami 0 0 +sna sn Shona 0 0 +snd sd Sindhi 0 0 +snk Soninke 0 0 +sog Sogdian 0 0 +som so Somali 0 0 +son Songhai 0 0 +sot st Sotho, Southern 0 0 +spa es Spanish 1 1 +srd sc Sardinian 0 0 +srr Serer 0 0 +ssa Nilo-Saharan (Other) 0 0 +ssw ss Swati 0 0 +suk Sukuma 0 0 +sun su Sundanese 0 0 +sus Susu 0 0 +sux Sumerian 0 0 +swa sw Swahili 1 0 +swe sv Swedish 1 1 +syr Syriac 1 0 +tah ty Tahitian 0 0 +tai Tai (Other) 0 0 +tam ta Tamil 0 0 +tat tt Tatar 0 0 +tel te Telugu 0 0 +tem Timne 0 0 +ter Tereno 0 0 +tet Tetum 0 0 +tgk tg Tajik 0 0 +tgl tl Tagalog 1 1 +tha th Thai 1 1 +tib bo Tibetan 0 0 +tig Tigre 0 0 +tir ti Tigrinya 0 0 +tiv Tiv 0 0 +tkl Tokelau 0 0 +tlh Klingon 0 0 +tli Tlingit 0 0 +tmh Tamashek 0 0 +tog Tonga (Nyasa) 0 0 +ton to Tonga (Tonga Islands) 0 0 +tpi Tok Pisin 0 0 +tsi Tsimshian 0 0 +tsn tn Tswana 0 0 +tso ts Tsonga 0 0 +tuk tk Turkmen 0 0 +tum Tumbuka 0 0 +tup Tupi languages 0 0 +tur tr Turkish 1 1 +tut Altaic (Other) 0 0 +tvl Tuvalu 0 0 +twi tw Twi 0 0 +tyv Tuvinian 0 0 +udm Udmurt 0 0 +uga Ugaritic 0 0 +uig ug Uighur 0 0 +ukr uk Ukrainian 1 1 +umb Umbundu 0 0 +und Undetermined 0 0 +urd ur Urdu 1 0 +uzb uz Uzbek 0 0 +vai Vai 0 0 +ven ve Venda 0 0 +vie vi Vietnamese 1 1 +vol vo Volapük 0 0 +vot Votic 0 0 +wak Wakashan languages 0 0 +wal Walamo 0 0 +war Waray 0 0 +was Washo 0 0 +wel cy Welsh 0 0 +wen Sorbian languages 0 0 +wln wa Walloon 0 0 +wol wo Wolof 0 0 +xal Kalmyk 0 0 +xho xh Xhosa 0 0 +yao Yao 0 0 +yap Yapese 0 0 +yid yi Yiddish 0 0 +yor yo Yoruba 0 0 +ypk Yupik languages 0 0 +zap Zapotec 0 0 +zen Zenaga 0 0 +zha za Zhuang 0 0 +znd Zande 0 0 +zul zu Zulu 0 0 +zun Zuni 0 0 +rum ro Romanian 1 1 +pob pb Brazilian 1 1 diff --git a/lib/guessit/test/test_api.py b/lib/guessit/test/test_api.py new file mode 100644 index 00000000..92cef41b --- /dev/null +++ b/lib/guessit/test/test_api.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# GuessIt - A library for guessing information from filenames +# Copyright (c) 2014 Nicolas Wack +# +# GuessIt is free software; you can redistribute it and/or modify it under +# the terms of the Lesser GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GuessIt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Lesser GNU General Public License for more details. +# +# You should have received a copy of the Lesser GNU General Public License +# along with this program. If not, see . +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +from guessit.test.guessittest import * + + +class TestApi(TestGuessit): + def test_api(self): + movie_path = 'Movies/Dark City (1998)/Dark.City.(1998).DC.BDRip.720p.DTS.X264-CHD.mkv' + + movie_info = guessit.guess_movie_info(movie_path) + video_info = guessit.guess_video_info(movie_path) + episode_info = guessit.guess_episode_info(movie_path) + file_info = guessit.guess_file_info(movie_path) + + self.assertEqual(guessit.guess_file_info(movie_path, type='movie'), movie_info) + self.assertEqual(guessit.guess_file_info(movie_path, type='video'), video_info) + self.assertEqual(guessit.guess_file_info(movie_path, type='episode'), episode_info) + + self.assertEqual(guessit.guess_file_info(movie_path, options={'type': 'movie'}), movie_info) + self.assertEqual(guessit.guess_file_info(movie_path, options={'type': 'video'}), video_info) + self.assertEqual(guessit.guess_file_info(movie_path, options={'type': 'episode'}), episode_info) + + self.assertEqual(guessit.guess_file_info(movie_path, options={'type': 'episode'}, type='movie'), episode_info) # kwargs priority other options + + movie_path_name_only = 'Movies/Dark City (1998)/Dark.City.(1998).DC.BDRip.720p.DTS.X264-CHD' + file_info_name_only = guessit.guess_file_info(movie_path_name_only, options={"name_only": True}) + + self.assertFalse('container' in file_info_name_only) + self.assertTrue('container' in file_info) + +suite = allTests(TestApi) + +if __name__ == '__main__': + TextTestRunner(verbosity=2).run(suite) diff --git a/lib/guessit/test/test_autodetect.py b/lib/guessit/test/test_autodetect.py new file mode 100644 index 00000000..229b491f --- /dev/null +++ b/lib/guessit/test/test_autodetect.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# GuessIt - A library for guessing information from filenames +# Copyright (c) 2013 Nicolas Wack +# +# GuessIt is free software; you can redistribute it and/or modify it under +# the terms of the Lesser GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GuessIt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Lesser GNU General Public License for more details. +# +# You should have received a copy of the Lesser GNU General Public License +# along with this program. If not, see . +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +from guessit.test.guessittest import * + + +class TestAutoDetect(TestGuessit): + def testEmpty(self): + result = guessit.guess_file_info('') + self.assertEqual(result, {}) + + result = guessit.guess_file_info('___-__') + self.assertEqual(result, {}) + + result = guessit.guess_file_info('__-.avc') + self.assertEqual(result, {'type': 'unknown', 'extension': 'avc'}) + + def testAutoDetect(self): + self.checkMinimumFieldsCorrect(filename='autodetect.yaml', + remove_type=False) + + +suite = allTests(TestAutoDetect) + +if __name__ == '__main__': + TextTestRunner(verbosity=2).run(suite) diff --git a/lib/guessit/test/test_autodetect_all.py b/lib/guessit/test/test_autodetect_all.py new file mode 100644 index 00000000..033e1571 --- /dev/null +++ b/lib/guessit/test/test_autodetect_all.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# GuessIt - A library for guessing information from filenames +# Copyright (c) 2013 Nicolas Wack +# +# GuessIt is free software; you can redistribute it and/or modify it under +# the terms of the Lesser GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GuessIt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Lesser GNU General Public License for more details. +# +# You should have received a copy of the Lesser GNU General Public License +# along with this program. If not, see . +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +from guessit.test.guessittest import * + +IGNORE_EPISODES = [] +IGNORE_MOVIES = [] + + +class TestAutoDetectAll(TestGuessit): + def testAutoMatcher(self): + self.checkMinimumFieldsCorrect(filename='autodetect.yaml', + remove_type=False) + + def testAutoMatcherMovies(self): + self.checkMinimumFieldsCorrect(filename='movies.yaml', + exclude_files=IGNORE_MOVIES) + + def testAutoMatcherEpisodes(self): + self.checkMinimumFieldsCorrect(filename='episodes.yaml', + exclude_files=IGNORE_EPISODES) + + +suite = allTests(TestAutoDetectAll) + +if __name__ == '__main__': + TextTestRunner(verbosity=2).run(suite) diff --git a/lib/guessit/test/test_doctests.py b/lib/guessit/test/test_doctests.py new file mode 100644 index 00000000..9fedeb0f --- /dev/null +++ b/lib/guessit/test/test_doctests.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# GuessIt - A library for guessing information from filenames +# Copyright (c) 2014 Nicolas Wack +# +# GuessIt is free software; you can redistribute it and/or modify it under +# the terms of the Lesser GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GuessIt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Lesser GNU General Public License for more details. +# +# You should have received a copy of the Lesser GNU General Public License +# along with this program. If not, see . +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +from guessit.test.guessittest import * +import guessit +import guessit.hash_ed2k +import unittest +import doctest + + +def load_tests(loader, tests, ignore): + tests.addTests(doctest.DocTestSuite(guessit)) + tests.addTests(doctest.DocTestSuite(guessit.date)) + tests.addTests(doctest.DocTestSuite(guessit.fileutils)) + tests.addTests(doctest.DocTestSuite(guessit.guess)) + tests.addTests(doctest.DocTestSuite(guessit.hash_ed2k)) + tests.addTests(doctest.DocTestSuite(guessit.language)) + tests.addTests(doctest.DocTestSuite(guessit.matchtree)) + tests.addTests(doctest.DocTestSuite(guessit.textutils)) + return tests + +suite = unittest.TestSuite() +load_tests(None, suite, None) + +if __name__ == '__main__': + TextTestRunner(verbosity=2).run(suite) diff --git a/lib/guessit/test/test_episode.py b/lib/guessit/test/test_episode.py new file mode 100644 index 00000000..03abf6b0 --- /dev/null +++ b/lib/guessit/test/test_episode.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# GuessIt - A library for guessing information from filenames +# Copyright (c) 2013 Nicolas Wack +# +# GuessIt is free software; you can redistribute it and/or modify it under +# the terms of the Lesser GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GuessIt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Lesser GNU General Public License for more details. +# +# You should have received a copy of the Lesser GNU General Public License +# along with this program. If not, see . +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +from guessit.test.guessittest import * + + +class TestEpisode(TestGuessit): + def testEpisodes(self): + self.checkMinimumFieldsCorrect(filetype='episode', + filename='episodes.yaml') + + +suite = allTests(TestEpisode) + +if __name__ == '__main__': + TextTestRunner(verbosity=2).run(suite) diff --git a/lib/guessit/test/test_hashes.py b/lib/guessit/test/test_hashes.py new file mode 100644 index 00000000..a8bc763c --- /dev/null +++ b/lib/guessit/test/test_hashes.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# GuessIt - A library for guessing information from filenames +# Copyright (c) 2013 Nicolas Wack +# +# GuessIt is free software; you can redistribute it and/or modify it under +# the terms of the Lesser GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GuessIt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Lesser GNU General Public License for more details. +# +# You should have received a copy of the Lesser GNU General Public License +# along with this program. If not, see . +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +from guessit.test.guessittest import * + + +class TestHashes(TestGuessit): + def test_hashes(self): + hashes = ( + ('hash_mpc', '1MB', u'8542ad406c15c8bd'), # TODO: Check if this value is valid + ('hash_ed2k', '1MB', u'ed2k://|file|1MB|1048576|AA3CC5552A9931A76B61A41D306735F7|/'), # TODO: Check if this value is valid + ('hash_md5', '1MB', u'5d8dcbca8d8ac21766f28797d6c3954c'), + ('hash_sha1', '1MB', u'51d2b8f3248d7ee495b7750c8da5aa3b3819de9d'), + ('hash_md5', 'dummy.srt', u'64de6b5893cac24456c46a935ef9c359'), + ('hash_sha1', 'dummy.srt', u'a703fc0fa4518080505809bf562c6fc6f7b3c98c') + ) + + for hash_type, filename, expected_value in hashes: + guess = guess_file_info(file_in_same_dir(__file__, filename), hash_type) + computed_value = guess.get(hash_type) + self.assertEqual(expected_value, guess.get(hash_type), "Invalid %s for %s: %s != %s" % (hash_type, filename, computed_value, expected_value)) + + +suite = allTests(TestHashes) + +if __name__ == '__main__': + TextTestRunner(verbosity=2).run(suite) diff --git a/lib/guessit/test/test_language.py b/lib/guessit/test/test_language.py new file mode 100644 index 00000000..406d92c1 --- /dev/null +++ b/lib/guessit/test/test_language.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# GuessIt - A library for guessing information from filenames +# Copyright (c) 2013 Nicolas Wack +# +# GuessIt is free software; you can redistribute it and/or modify it under +# the terms of the Lesser GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GuessIt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Lesser GNU General Public License for more details. +# +# You should have received a copy of the Lesser GNU General Public License +# along with this program. If not, see . +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +from guessit.test.guessittest import * + +import io + + +class TestLanguage(TestGuessit): + + def check_languages(self, languages): + for lang1, lang2 in languages.items(): + self.assertEqual(Language(lang1), + Language(lang2)) + + def test_addic7ed(self): + languages = {'English': 'en', + 'English (US)': 'en', + 'English (UK)': 'en', + 'Italian': 'it', + 'Portuguese': 'pt', + 'Portuguese (Brazilian)': 'pt', + 'Romanian': 'ro', + 'Español (Latinoamérica)': 'es', + 'Español (España)': 'es', + 'Spanish (Latin America)': 'es', + 'Español': 'es', + 'Spanish': 'es', + 'Spanish (Spain)': 'es', + 'French': 'fr', + 'Greek': 'el', + 'Arabic': 'ar', + 'German': 'de', + 'Croatian': 'hr', + 'Indonesian': 'id', + 'Hebrew': 'he', + 'Russian': 'ru', + 'Turkish': 'tr', + 'Swedish': 'se', + 'Czech': 'cs', + 'Dutch': 'nl', + 'Hungarian': 'hu', + 'Norwegian': 'no', + 'Polish': 'pl', + 'Persian': 'fa'} + + self.check_languages(languages) + + def test_subswiki(self): + languages = {'English (US)': 'en', 'English (UK)': 'en', 'English': 'en', + 'French': 'fr', 'Brazilian': 'po', 'Portuguese': 'pt', + 'Español (Latinoamérica)': 'es', 'Español (España)': 'es', + 'Español': 'es', 'Italian': 'it', 'Català': 'ca'} + + self.check_languages(languages) + + def test_tvsubtitles(self): + languages = {'English': 'en', 'Español': 'es', 'French': 'fr', 'German': 'de', + 'Brazilian': 'br', 'Russian': 'ru', 'Ukrainian': 'ua', 'Italian': 'it', + 'Greek': 'gr', 'Arabic': 'ar', 'Hungarian': 'hu', 'Polish': 'pl', + 'Turkish': 'tr', 'Dutch': 'nl', 'Portuguese': 'pt', 'Swedish': 'sv', + 'Danish': 'da', 'Finnish': 'fi', 'Korean': 'ko', 'Chinese': 'cn', + 'Japanese': 'jp', 'Bulgarian': 'bg', 'Czech': 'cz', 'Romanian': 'ro'} + + self.check_languages(languages) + + def test_opensubtitles(self): + opensubtitles_langfile = file_in_same_dir(__file__, 'opensubtitles_languages_2012_05_09.txt') + for l in [u(l).strip() for l in io.open(opensubtitles_langfile, encoding='utf-8')][1:]: + idlang, alpha2, _, upload_enabled, web_enabled = l.strip().split('\t') + # do not test languages that are too esoteric / not widely available + if int(upload_enabled) and int(web_enabled): + # check that we recognize the opensubtitles language code correctly + # and that we are able to output this code from a language + self.assertEqual(idlang, Language(idlang).opensubtitles) + if alpha2: + # check we recognize the opensubtitles 2-letter code correctly + self.check_languages({idlang: alpha2}) + + def test_tmdb(self): + # examples from http://api.themoviedb.org/2.1/language-tags + for lang in ['en-US', 'en-CA', 'es-MX', 'fr-PF']: + self.assertEqual(lang, Language(lang).tmdb) + + def test_subtitulos(self): + languages = {'English (US)': 'en', 'English (UK)': 'en', 'English': 'en', + 'French': 'fr', 'Brazilian': 'po', 'Portuguese': 'pt', + 'Español (Latinoamérica)': 'es', 'Español (España)': 'es', + 'Español': 'es', 'Italian': 'it', 'Català': 'ca'} + + self.check_languages(languages) + + def test_thesubdb(self): + languages = {'af': 'af', 'cs': 'cs', 'da': 'da', 'de': 'de', 'en': 'en', 'es': 'es', 'fi': 'fi', + 'fr': 'fr', 'hu': 'hu', 'id': 'id', 'it': 'it', 'la': 'la', 'nl': 'nl', 'no': 'no', + 'oc': 'oc', 'pl': 'pl', 'pt': 'pt', 'ro': 'ro', 'ru': 'ru', 'sl': 'sl', 'sr': 'sr', + 'sv': 'sv', 'tr': 'tr'} + + self.check_languages(languages) + + def test_language_object(self): + self.assertEqual(len(list(set([Language('qwerty'), Language('asdf')]))), 1) + d = {Language('qwerty'): 7} + d[Language('asdf')] = 23 + self.assertEqual(d[Language('qwerty')], 23) + + def test_exceptions(self): + self.assertEqual(Language('br'), Language('pt(br)')) + + # languages should be equal regardless of country + self.assertEqual(Language('br'), Language('pt')) + + self.assertEqual(Language('unknown'), Language('und')) + + +suite = allTests(TestLanguage) + +if __name__ == '__main__': + TextTestRunner(verbosity=2).run(suite) diff --git a/lib/guessit/test/test_main.py b/lib/guessit/test/test_main.py new file mode 100644 index 00000000..19540d8a --- /dev/null +++ b/lib/guessit/test/test_main.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# GuessIt - A library for guessing information from filenames +# Copyright (c) 2014 Nicolas Wack +# +# GuessIt is free software; you can redistribute it and/or modify it under +# the terms of the Lesser GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GuessIt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Lesser GNU General Public License for more details. +# +# You should have received a copy of the Lesser GNU General Public License +# along with this program. If not, see . +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +from guessit.test.guessittest import * +from guessit.fileutils import split_path, file_in_same_dir +from guessit.textutils import strip_brackets, str_replace, str_fill +from guessit import PY2 +from guessit import __main__ + +if PY2: + from StringIO import StringIO +else: + from io import StringIO + + +class TestMain(TestGuessit): + def setUp(self): + self._stdout = sys.stdout + string_out = StringIO() + sys.stdout = string_out + + def tearDown(self): + sys.stdout = self._stdout + + def test_list_properties(self): + __main__.main(["-p"], False) + __main__.main(["-l"], False) + + def test_list_transformers(self): + __main__.main(["--transformers"], False) + __main__.main(["-l", "--transformers"], False) + + def test_demo(self): + __main__.main(["-d"], False) + __main__.main(["-l"], False) + + def test_filename(self): + __main__.main(["A.Movie.2014.avi"], False) + __main__.main(["A.Movie.2014.avi", "A.2nd.Movie.2014.avi"], False) + __main__.main(["-y", "A.Movie.2014.avi"], False) + __main__.main(["-a", "A.Movie.2014.avi"], False) + __main__.main(["-v", "A.Movie.2014.avi"], False) + __main__.main(["-t", "movie", "A.Movie.2014.avi"], False) + __main__.main(["-t", "episode", "A.Serie.S02E06.avi"], False) + __main__.main(["-i", "hash_mpc", file_in_same_dir(__file__, "1MB")], False) + __main__.main(["-i", "hash_md5", file_in_same_dir(__file__, "1MB")], False) + +suite = allTests(TestMain) + +if __name__ == '__main__': + TextTestRunner(verbosity=2).run(suite) diff --git a/lib/guessit/test/test_matchtree.py b/lib/guessit/test/test_matchtree.py new file mode 100644 index 00000000..c10840a0 --- /dev/null +++ b/lib/guessit/test/test_matchtree.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# GuessIt - A library for guessing information from filenames +# Copyright (c) 2013 Nicolas Wack +# +# GuessIt is free software; you can redistribute it and/or modify it under +# the terms of the Lesser GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GuessIt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Lesser GNU General Public License for more details. +# +# You should have received a copy of the Lesser GNU General Public License +# along with this program. If not, see . +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +from guessit.test.guessittest import * + +from guessit.transfo.guess_release_group import GuessReleaseGroup +from guessit.transfo.guess_properties import GuessProperties +from guessit.matchtree import BaseMatchTree + +keywords = yaml.load(""" + +? Xvid PROPER +: videoCodec: Xvid + other: PROPER + +? PROPER-Xvid +: videoCodec: Xvid + other: PROPER + +""") + + +def guess_info(string, options=None): + mtree = MatchTree(string) + GuessReleaseGroup().process(mtree, options) + GuessProperties().process(mtree, options) + return mtree.matched() + + +class TestMatchTree(TestGuessit): + def test_base_tree(self): + t = BaseMatchTree('One Two Three(Three) Four') + t.partition((3, 7, 20)) + leaves = t.leaves() + + self.assertEqual(leaves[0].span, (0, 3)) + + self.assertEqual('One', leaves[0].value) + self.assertEqual(' Two', leaves[1].value) + self.assertEqual(' Three(Three)', leaves[2].value) + self.assertEqual(' Four', leaves[3].value) + + leaves[2].partition((1, 6, 7, 12)) + three_leaves = leaves[2].leaves() + + self.assertEqual('Three', three_leaves[1].value) + self.assertEqual('Three', three_leaves[3].value) + + leaves = t.leaves() + + self.assertEqual(len(leaves), 8) + + self.assertEqual(leaves[5], three_leaves[3]) + + self.assertEqual(t.previous_leaf(leaves[5]), leaves[4]) + self.assertEqual(t.next_leaf(leaves[5]), leaves[6]) + + self.assertEqual(t.next_leaves(leaves[5]), [leaves[6], leaves[7]]) + self.assertEqual(t.previous_leaves(leaves[5]), [leaves[4], leaves[3], leaves[2], leaves[1], leaves[0]]) + + self.assertEqual(t.next_leaf(leaves[7]), None) + self.assertEqual(t.previous_leaf(leaves[0]), None) + + self.assertEqual(t.next_leaves(leaves[7]), []) + self.assertEqual(t.previous_leaves(leaves[0]), []) + + def test_match(self): + self.checkFields(keywords, guess_info) + + +suite = allTests(TestMatchTree) + +if __name__ == '__main__': + TextTestRunner(verbosity=2).run(suite) diff --git a/lib/guessit/test/test_movie.py b/lib/guessit/test/test_movie.py new file mode 100644 index 00000000..eecbf49d --- /dev/null +++ b/lib/guessit/test/test_movie.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# GuessIt - A library for guessing information from filenames +# Copyright (c) 2013 Nicolas Wack +# +# GuessIt is free software; you can redistribute it and/or modify it under +# the terms of the Lesser GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GuessIt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Lesser GNU General Public License for more details. +# +# You should have received a copy of the Lesser GNU General Public License +# along with this program. If not, see . +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +from guessit.test.guessittest import * + + +class TestMovie(TestGuessit): + def testMovies(self): + self.checkMinimumFieldsCorrect(filetype='movie', + filename='movies.yaml') + + +suite = allTests(TestMovie) + +if __name__ == '__main__': + TextTestRunner(verbosity=2).run(suite) diff --git a/lib/guessit/test/test_quality.py b/lib/guessit/test/test_quality.py new file mode 100644 index 00000000..52e21791 --- /dev/null +++ b/lib/guessit/test/test_quality.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# GuessIt - A library for guessing information from filenames +# Copyright (c) 2013 Nicolas Wack +# +# GuessIt is free software; you can redistribute it and/or modify it under +# the terms of the Lesser GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GuessIt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Lesser GNU General Public License for more details. +# +# You should have received a copy of the Lesser GNU General Public License +# along with this program. If not, see . +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +from guessit.quality import best_quality, best_quality_properties +from guessit.containers import QualitiesContainer +from guessit.test.guessittest import * + + +class TestQuality(TestGuessit): + def test_container(self): + container = QualitiesContainer() + + container.register_quality('color', 'red', 10) + container.register_quality('color', 'orange', 20) + container.register_quality('color', 'green', 30) + + container.register_quality('context', 'sun', 100) + container.register_quality('context', 'sea', 200) + container.register_quality('context', 'sex', 300) + + g1 = Guess() + g1['color'] = 'red' + + g2 = Guess() + g2['color'] = 'green' + + g3 = Guess() + g3['color'] = 'orange' + + q3 = container.rate_quality(g3) + self.assertEqual(q3, 20, "ORANGE should be rated 20. Don't ask why!") + + q1 = container.rate_quality(g1) + q2 = container.rate_quality(g2) + + self.assertTrue(q2 > q1, "GREEN should be greater than RED. Don't ask why!") + + g1['context'] = 'sex' + g2['context'] = 'sun' + + q1 = container.rate_quality(g1) + q2 = container.rate_quality(g2) + + self.assertTrue(q1 > q2, "SEX should be greater than SUN. Don't ask why!") + + self.assertEqual(container.best_quality(g1, g2), g1, "RED&SEX should be better than GREEN&SUN. Don't ask why!") + + self.assertEqual(container.best_quality_properties(['color'], g1, g2), g2, "GREEN should be better than RED. Don't ask why!") + + self.assertEqual(container.best_quality_properties(['context'], g1, g2), g1, "SEX should be better than SUN. Don't ask why!") + + q1 = container.rate_quality(g1, 'color') + q2 = container.rate_quality(g2, 'color') + + self.assertTrue(q2 > q1, "GREEN should be greater than RED. Don't ask why!") + + container.unregister_quality('context', 'sex') + container.unregister_quality('context', 'sun') + + q1 = container.rate_quality(g1) + q2 = container.rate_quality(g2) + + self.assertTrue(q2 > q1, "GREEN&SUN should be greater than RED&SEX. Don't ask why!") + + g3['context'] = 'sea' + container.unregister_quality('context', 'sea') + + q3 = container.rate_quality(g3, 'context') + self.assertEqual(q3, 0, "Context should be unregistered.") + + container.unregister_quality('color') + q3 = container.rate_quality(g3, 'color') + + self.assertEqual(q3, 0, "Color should be unregistered.") + + container.clear_qualities() + + q1 = container.rate_quality(g1) + q2 = container.rate_quality(g2) + + self.assertTrue(q1 == q2 == 0, "Empty quality container should rate each guess to 0") + + def test_quality_transformers(self): + guess_720p = guessit.guess_file_info("2012.2009.720p.BluRay.x264.DTS WiKi.mkv") + guess_1080p = guessit.guess_file_info("2012.2009.1080p.BluRay.x264.MP3 WiKi.mkv") + + self.assertTrue('audioCodec' in guess_720p, "audioCodec should be present") + self.assertTrue('audioCodec' in guess_1080p, "audioCodec should be present") + self.assertTrue('screenSize' in guess_720p, "screenSize should be present") + self.assertTrue('screenSize' in guess_1080p, "screenSize should be present") + + best_quality_guess = best_quality(guess_720p, guess_1080p) + + self.assertTrue(guess_1080p == best_quality_guess, "1080p+MP3 is not the best global quality") + + best_quality_guess = best_quality_properties(['screenSize'], guess_720p, guess_1080p) + + self.assertTrue(guess_1080p == best_quality_guess, "1080p is not the best screenSize") + + best_quality_guess = best_quality_properties(['audioCodec'], guess_720p, guess_1080p) + + self.assertTrue(guess_720p == best_quality_guess, "DTS is not the best audioCodec") + +suite = allTests(TestQuality) + +if __name__ == '__main__': + TextTestRunner(verbosity=2).run(suite) diff --git a/lib/guessit/test/test_utils.py b/lib/guessit/test/test_utils.py new file mode 100644 index 00000000..8cf4028b --- /dev/null +++ b/lib/guessit/test/test_utils.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# GuessIt - A library for guessing information from filenames +# Copyright (c) 2013 Nicolas Wack +# +# GuessIt is free software; you can redistribute it and/or modify it under +# the terms of the Lesser GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GuessIt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Lesser GNU General Public License for more details. +# +# You should have received a copy of the Lesser GNU General Public License +# along with this program. If not, see . +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +from guessit.test.guessittest import * +from guessit.fileutils import split_path +from guessit.textutils import strip_brackets, str_replace, str_fill, from_camel, is_camel,\ + levenshtein, reorder_title +from guessit import PY2 +from guessit.date import search_date, search_year +from datetime import datetime, date, timedelta + + +class TestUtils(TestGuessit): + def test_splitpath(self): + alltests = {False: {'/usr/bin/smewt': ['/', 'usr', 'bin', 'smewt'], + 'relative_path/to/my_folder/': ['relative_path', 'to', 'my_folder'], + '//some/path': ['//', 'some', 'path'], + '//some//path': ['//', 'some', 'path'], + '///some////path': ['///', 'some', 'path'] + + }, + True: {'C:\\Program Files\\Smewt\\smewt.exe': ['C:\\', 'Program Files', 'Smewt', 'smewt.exe'], + 'Documents and Settings\\User\\config': ['Documents and Settings', 'User', 'config'], + 'C:\\Documents and Settings\\User\\config': ['C:\\', 'Documents and Settings', 'User', 'config'], + # http://bugs.python.org/issue19945 + '\\\\netdrive\\share': ['\\\\', 'netdrive', 'share'] if PY2 else ['\\\\netdrive\\share'], + '\\\\netdrive\\share\\folder': ['\\\\', 'netdrive', 'share', 'folder'] if PY2 else ['\\\\netdrive\\share\\', 'folder'], + } + } + tests = alltests[sys.platform == 'win32'] + for path, split in tests.items(): + self.assertEqual(split, split_path(path)) + + def test_strip_brackets(self): + allTests = (('', ''), + ('[test]', 'test'), + ('{test2}', 'test2'), + ('(test3)', 'test3'), + ('(test4]', '(test4]'), + ) + + for i, e in allTests: + self.assertEqual(e, strip_brackets(i)) + + def test_levenshtein(self): + self.assertEqual(levenshtein("abcdef ghijk lmno", "abcdef ghijk lmno"), 0) + self.assertEqual(levenshtein("abcdef ghijk lmnop", "abcdef ghijk lmno"), 1) + self.assertEqual(levenshtein("abcdef ghijk lmno", "abcdef ghijk lmn"), 1) + self.assertEqual(levenshtein("abcdef ghijk lmno", "abcdef ghijk lmnp"), 1) + self.assertEqual(levenshtein("abcdef ghijk lmno", "abcdef ghijk lmnq"), 1) + self.assertEqual(levenshtein("cbcdef ghijk lmno", "abcdef ghijk lmnq"), 2) + self.assertEqual(levenshtein("cbcdef ghihk lmno", "abcdef ghijk lmnq"), 3) + + def test_reorder_title(self): + self.assertEqual(reorder_title("Simpsons, The"), "The Simpsons") + self.assertEqual(reorder_title("Simpsons,The"), "The Simpsons") + self.assertEqual(reorder_title("Simpsons,Les", articles=('the', 'le', 'la', 'les')), "Les Simpsons") + self.assertEqual(reorder_title("Simpsons, Les", articles=('the', 'le', 'la', 'les')), "Les Simpsons") + + def test_camel(self): + self.assertEqual("", from_camel("")) + + self.assertEqual("Hello world", str_replace("Hello World", 6, 'w')) + self.assertEqual("Hello *****", str_fill("Hello World", (6, 11), '*')) + + self.assertTrue("This is camel", from_camel("ThisIsCamel")) + + self.assertEqual('camel case', from_camel('camelCase')) + self.assertEqual('A case', from_camel('ACase')) + self.assertEqual('MiXedCaSe is not camel case', from_camel('MiXedCaSe is not camelCase')) + + self.assertEqual("This is camel cased title", from_camel("ThisIsCamelCasedTitle")) + self.assertEqual("This is camel CASED title", from_camel("ThisIsCamelCASEDTitle")) + + self.assertEqual("These are camel CASED title", from_camel("TheseAreCamelCASEDTitle")) + + self.assertEqual("Give a camel case string", from_camel("GiveACamelCaseString")) + + self.assertEqual("Death TO camel case", from_camel("DeathTOCamelCase")) + self.assertEqual("But i like java too:)", from_camel("ButILikeJavaToo:)")) + + self.assertEqual("Beatdown french DVD rip.mkv", from_camel("BeatdownFrenchDVDRip.mkv")) + self.assertEqual("DO NOTHING ON UPPER CASE", from_camel("DO NOTHING ON UPPER CASE")) + + self.assertFalse(is_camel("this_is_not_camel")) + self.assertTrue(is_camel("ThisIsCamel")) + + self.assertEqual("Dark.City.(1998).DC.BDRIP.720p.DTS.X264-CHD.mkv", from_camel("Dark.City.(1998).DC.BDRIP.720p.DTS.X264-CHD.mkv")) + self.assertFalse(is_camel("Dark.City.(1998).DC.BDRIP.720p.DTS.X264-CHD.mkv")) + + self.assertEqual("A2LiNE", from_camel("A2LiNE")) + + def test_date(self): + self.assertEqual(search_year(' in the year 2000... '), (2000, (13, 17))) + self.assertEqual(search_year(' they arrived in 1492. '), (None, None)) + + today = date.today() + today_year_2 = int(str(today.year)[2:]) + + future = today + timedelta(days=1000) + future_year_2 = int(str(future.year)[2:]) + + past = today - timedelta(days=10000) + past_year_2 = int(str(past.year)[2:]) + + self.assertEqual(search_date(' Something before 2002-04-22 '), (date(2002, 4, 22), (18, 28))) + self.assertEqual(search_date(' 2002-04-22 Something after '), (date(2002, 4, 22), (1, 11))) + + self.assertEqual(search_date(' This happened on 2002-04-22. '), (date(2002, 4, 22), (18, 28))) + self.assertEqual(search_date(' This happened on 22-04-2002. '), (date(2002, 4, 22), (18, 28))) + + self.assertEqual(search_date(' This happened on 13-04-%s. ' % (today_year_2,)), (date(today.year, 4, 13), (18, 26))) + self.assertEqual(search_date(' This happened on 22-04-%s. ' % (future_year_2,)), (date(future.year, 4, 22), (18, 26))) + self.assertEqual(search_date(' This happened on 20-04-%s. ' % (past_year_2)), (date(past.year, 4, 20), (18, 26))) + + self.assertEqual(search_date(' This happened on 04-13-%s. ' % (today_year_2,)), (date(today.year, 4, 13), (18, 26))) + self.assertEqual(search_date(' This happened on 04-22-%s. ' % (future_year_2,)), (date(future.year, 4, 22), (18, 26))) + self.assertEqual(search_date(' This happened on 04-20-%s. ' % (past_year_2)), (date(past.year, 4, 20), (18, 26))) + + self.assertEqual(search_date(' This happened on 35-12-%s. ' % (today_year_2,)), (None, None)) + self.assertEqual(search_date(' This happened on 37-18-%s. ' % (future_year_2,)), (None, None)) + self.assertEqual(search_date(' This happened on 44-42-%s. ' % (past_year_2)), (None, None)) + + self.assertEqual(search_date(' This happened on %s. ' % (today, )), (today, (18, 28))) + self.assertEqual(search_date(' This happened on %s. ' % (future, )), (future, (18, 28))) + self.assertEqual(search_date(' This happened on %s. ' % (past, )), (past, (18, 28))) + + self.assertEqual(search_date(' released date: 04-03-1901? '), (None, None)) + + self.assertEqual(search_date(' There\'s no date in here. '), (None, None)) + + +suite = allTests(TestUtils) + +if __name__ == '__main__': + TextTestRunner(verbosity=2).run(suite) diff --git a/lib/stevedore/example/__init__.py b/lib/stevedore/example/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lib/stevedore/example/base.py b/lib/stevedore/example/base.py new file mode 100644 index 00000000..d128a536 --- /dev/null +++ b/lib/stevedore/example/base.py @@ -0,0 +1,21 @@ +import abc + + +class FormatterBase(object): + """Base class for example plugin used in the tutoral. + """ + + __metaclass__ = abc.ABCMeta + + def __init__(self, max_width=60): + self.max_width = max_width + + @abc.abstractmethod + def format(self, data): + """Format the data and return unicode text. + + :param data: A dictionary with string keys and simple types as + values. + :type data: dict(str:?) + :returns: Iterable producing the formatted text. + """ diff --git a/lib/stevedore/example/fields.py b/lib/stevedore/example/fields.py new file mode 100644 index 00000000..f5c8e194 --- /dev/null +++ b/lib/stevedore/example/fields.py @@ -0,0 +1,36 @@ +import textwrap + +from stevedore.example import base + + +class FieldList(base.FormatterBase): + """Format values as a reStructuredText field list. + + For example:: + + : name1 : value + : name2 : value + : name3 : a long value + will be wrapped with + a hanging indent + """ + + def format(self, data): + """Format the data and return unicode text. + + :param data: A dictionary with string keys and simple types as + values. + :type data: dict(str:?) + """ + for name, value in sorted(data.items()): + full_text = ': {name} : {value}'.format( + name=name, + value=value, + ) + wrapped_text = textwrap.fill( + full_text, + initial_indent='', + subsequent_indent=' ', + width=self.max_width, + ) + yield wrapped_text + '\n' diff --git a/lib/stevedore/example/load_as_driver.py b/lib/stevedore/example/load_as_driver.py new file mode 100644 index 00000000..d8c47f5f --- /dev/null +++ b/lib/stevedore/example/load_as_driver.py @@ -0,0 +1,37 @@ +from __future__ import print_function + +import argparse + +from stevedore import driver + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + 'format', + nargs='?', + default='simple', + help='the output format', + ) + parser.add_argument( + '--width', + default=60, + type=int, + help='maximum output width for text', + ) + parsed_args = parser.parse_args() + + data = { + 'a': 'A', + 'b': 'B', + 'long': 'word ' * 80, + } + + mgr = driver.DriverManager( + namespace='stevedore.example.formatter', + name=parsed_args.format, + invoke_on_load=True, + invoke_args=(parsed_args.width,), + ) + for chunk in mgr.driver.format(data): + print(chunk, end='') diff --git a/lib/stevedore/example/load_as_extension.py b/lib/stevedore/example/load_as_extension.py new file mode 100644 index 00000000..436206a3 --- /dev/null +++ b/lib/stevedore/example/load_as_extension.py @@ -0,0 +1,39 @@ +from __future__ import print_function + +import argparse + +from stevedore import extension + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + '--width', + default=60, + type=int, + help='maximum output width for text', + ) + parsed_args = parser.parse_args() + + data = { + 'a': 'A', + 'b': 'B', + 'long': 'word ' * 80, + } + + mgr = extension.ExtensionManager( + namespace='stevedore.example.formatter', + invoke_on_load=True, + invoke_args=(parsed_args.width,), + ) + + def format_data(ext, data): + return (ext.name, ext.obj.format(data)) + + results = mgr.map(format_data, data) + + for name, result in results: + print('Formatter: {0}'.format(name)) + for chunk in result: + print(chunk, end='') + print('') diff --git a/lib/stevedore/example/setup.py b/lib/stevedore/example/setup.py new file mode 100644 index 00000000..bac1c1fd --- /dev/null +++ b/lib/stevedore/example/setup.py @@ -0,0 +1,46 @@ +from setuptools import setup, find_packages + +setup( + name='stevedore-examples', + version='1.0', + + description='Demonstration package for stevedore', + + author='Doug Hellmann', + author_email='doug.hellmann@dreamhost.com', + + url='https://github.com/dreamhost/stevedore', + download_url='https://github.com/dreamhost/stevedore/tarball/master', + + classifiers=['Development Status :: 3 - Alpha', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3.3', + 'Intended Audience :: Developers', + 'Environment :: Console', + ], + + platforms=['Any'], + + scripts=[], + + provides=['stevedore.examples', + ], + + packages=find_packages(), + include_package_data=True, + + entry_points={ + 'stevedore.example.formatter': [ + 'simple = stevedore.example.simple:Simple', + 'field = stevedore.example.fields:FieldList', + 'plain = stevedore.example.simple:Simple', + ], + }, + + zip_safe=False, +) diff --git a/lib/stevedore/example/simple.py b/lib/stevedore/example/simple.py new file mode 100644 index 00000000..1cad96af --- /dev/null +++ b/lib/stevedore/example/simple.py @@ -0,0 +1,20 @@ +from stevedore.example import base + + +class Simple(base.FormatterBase): + """A very basic formatter. + """ + + def format(self, data): + """Format the data and return unicode text. + + :param data: A dictionary with string keys and simple types as + values. + :type data: dict(str:?) + """ + for name, value in sorted(data.items()): + line = '{name} = {value}\n'.format( + name=name, + value=value, + ) + yield line diff --git a/lib/stevedore/tests/__init__.py b/lib/stevedore/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lib/stevedore/tests/manager.py b/lib/stevedore/tests/manager.py new file mode 100644 index 00000000..28c37321 --- /dev/null +++ b/lib/stevedore/tests/manager.py @@ -0,0 +1,59 @@ +"""TestExtensionManager + +Extension manager used only for testing. +""" + +import logging +import warnings + +from stevedore import extension + + +LOG = logging.getLogger(__name__) + + +class TestExtensionManager(extension.ExtensionManager): + """ExtensionManager that is explicitly initialized for tests. + + .. deprecated:: 0.13 + + Use the :func:`make_test_instance` class method of the class + being replaced by the test instance instead of using this class + directly. + + :param extensions: Pre-configured Extension instances to use + instead of loading them from entry points. + :type extensions: list of :class:`~stevedore.extension.Extension` + :param namespace: The namespace for the entry points. + :type namespace: str + :param invoke_on_load: Boolean controlling whether to invoke the + object returned by the entry point after the driver is loaded. + :type invoke_on_load: bool + :param invoke_args: Positional arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_args: tuple + :param invoke_kwds: Named arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_kwds: dict + + """ + + def __init__(self, extensions, + namespace='test', + invoke_on_load=False, + invoke_args=(), + invoke_kwds={}): + super(TestExtensionManager, self).__init__(namespace, + invoke_on_load, + invoke_args, + invoke_kwds, + ) + self.extensions = extensions + warnings.warn( + 'TestExtesionManager has been replaced by make_test_instance()', + DeprecationWarning) + + def _load_plugins(self, *args, **kwds): + return [] diff --git a/lib/stevedore/tests/test_callback.py b/lib/stevedore/tests/test_callback.py new file mode 100644 index 00000000..d80c7f27 --- /dev/null +++ b/lib/stevedore/tests/test_callback.py @@ -0,0 +1,21 @@ +"""Tests for failure loading callback +""" + +from stevedore import extension + + +def test_extension_failure_custom_callback(): + errors = [] + + def failure_callback(manager, entrypoint, error): + errors.append((manager, entrypoint, error)) + + em = extension.ExtensionManager('stevedore.test.extension', + invoke_on_load=True, + on_load_failure_callback=failure_callback) + extensions = list(em.extensions) + assert len(extensions) > 0 + assert len(errors) == 1 + (manager, entrypoint, error) = errors[0] + assert manager is em + assert isinstance(error, IOError) diff --git a/lib/stevedore/tests/test_dispatch.py b/lib/stevedore/tests/test_dispatch.py new file mode 100644 index 00000000..7e1d8b08 --- /dev/null +++ b/lib/stevedore/tests/test_dispatch.py @@ -0,0 +1,103 @@ +from stevedore import dispatch + + +def check_dispatch(ep, *args, **kwds): + return ep.name == 't2' + + +def test_dispatch(): + + def invoke(ep, *args, **kwds): + return (ep.name, args, kwds) + + em = dispatch.DispatchExtensionManager( + 'stevedore.test.extension', + lambda *args, **kwds: True, + invoke_on_load=True, + invoke_args=('a',), + invoke_kwds={'b': 'B'}, + ) + assert len(em.extensions) == 2 + assert set(em.names()) == set(['t1', 't2']) + + results = em.map(check_dispatch, + invoke, + 'first', + named='named value', + ) + expected = [('t2', ('first',), {'named': 'named value'})] + assert results == expected + + +def test_dispatch_map_method(): + em = dispatch.DispatchExtensionManager( + 'stevedore.test.extension', + lambda *args, **kwds: True, + invoke_on_load=True, + invoke_args=('a',), + invoke_kwds={'b': 'B'}, + ) + + results = em.map_method(check_dispatch, 'get_args_and_data', + 'first') + assert results == [(('a',), {'b': 'B'}, 'first')] + + +def test_name_dispatch(): + + def invoke(ep, *args, **kwds): + return (ep.name, args, kwds) + + em = dispatch.NameDispatchExtensionManager( + 'stevedore.test.extension', + lambda *args, **kwds: True, + invoke_on_load=True, + invoke_args=('a',), + invoke_kwds={'b': 'B'}, + ) + assert len(em.extensions) == 2 + assert set(em.names()) == set(['t1', 't2']) + + results = em.map(['t2'], + invoke, + 'first', + named='named value', + ) + expected = [('t2', ('first',), {'named': 'named value'})] + assert results == expected + + +def test_name_dispatch_ignore_missing(): + + def invoke(ep, *args, **kwds): + return (ep.name, args, kwds) + + em = dispatch.NameDispatchExtensionManager( + 'stevedore.test.extension', + lambda *args, **kwds: True, + invoke_on_load=True, + invoke_args=('a',), + invoke_kwds={'b': 'B'}, + ) + + results = em.map(['t3', 't1'], + invoke, + 'first', + named='named value', + ) + expected = [('t1', ('first',), {'named': 'named value'})] + assert results == expected + + +def test_name_dispatch_map_method(): + em = dispatch.NameDispatchExtensionManager( + 'stevedore.test.extension', + lambda *args, **kwds: True, + invoke_on_load=True, + invoke_args=('a',), + invoke_kwds={'b': 'B'}, + ) + + results = em.map_method(['t3', 't1'], 'get_args_and_data', + 'first') + assert results == [(('a',), {'b': 'B'}, 'first')] diff --git a/lib/stevedore/tests/test_driver.py b/lib/stevedore/tests/test_driver.py new file mode 100644 index 00000000..e56d2c56 --- /dev/null +++ b/lib/stevedore/tests/test_driver.py @@ -0,0 +1,61 @@ +"""Tests for stevedore.extension +""" + +import mock +import pkg_resources + +from stevedore import driver +from stevedore.tests import test_extension + + +def test_detect_plugins(): + em = driver.DriverManager('stevedore.test.extension', 't1') + names = sorted(em.names()) + assert names == ['t1'] + + +def test_call(): + def invoke(ext, *args, **kwds): + return (ext.name, args, kwds) + em = driver.DriverManager('stevedore.test.extension', 't1') + result = em(invoke, 'a', b='C') + assert result == ('t1', ('a',), {'b': 'C'}) + + +def test_driver_property_not_invoked_on_load(): + em = driver.DriverManager('stevedore.test.extension', 't1', + invoke_on_load=False) + d = em.driver + assert d is test_extension.FauxExtension + + +def test_driver_property_invoked_on_load(): + em = driver.DriverManager('stevedore.test.extension', 't1', + invoke_on_load=True) + d = em.driver + assert isinstance(d, test_extension.FauxExtension) + + +def test_no_drivers(): + try: + driver.DriverManager('stevedore.test.extension.none', 't1') + except RuntimeError as err: + assert "No 'stevedore.test.extension.none' driver found" in str(err) + + +def test_multiple_drivers(): + # The idea for this test was contributed by clayg: + # https://gist.github.com/clayg/6311348 + fep_name = 'stevedore.extension.ExtensionManager._find_entry_points' + with mock.patch(fep_name) as fep: + fep.return_value = [ + pkg_resources.EntryPoint.parse('backend = pkg1:driver'), + pkg_resources.EntryPoint.parse('backend = pkg2:driver'), + ] + for ep in fep.return_value: + ep.load = lambda *args, **kwds: 'pkg backend' + try: + driver.DriverManager('stevedore.test.multiple_drivers', 'backend') + except RuntimeError as err: + assert "Multiple" in str(err), str(err) + fep.assert_called_with('stevedore.test.multiple_drivers') diff --git a/lib/stevedore/tests/test_enabled.py b/lib/stevedore/tests/test_enabled.py new file mode 100644 index 00000000..659e710e --- /dev/null +++ b/lib/stevedore/tests/test_enabled.py @@ -0,0 +1,29 @@ +from stevedore import enabled + + +def test_enabled(): + def check_enabled(ep): + return ep.name == 't2' + em = enabled.EnabledExtensionManager( + 'stevedore.test.extension', + check_enabled, + invoke_on_load=True, + invoke_args=('a',), + invoke_kwds={'b': 'B'}, + ) + assert len(em.extensions) == 1 + assert em.names() == ['t2'] + + +def test_enabled_after_load(): + def check_enabled(ext): + return ext.obj and ext.name == 't2' + em = enabled.EnabledExtensionManager( + 'stevedore.test.extension', + check_enabled, + invoke_on_load=True, + invoke_args=('a',), + invoke_kwds={'b': 'B'}, + ) + assert len(em.extensions) == 1 + assert em.names() == ['t2'] diff --git a/lib/stevedore/tests/test_example_fields.py b/lib/stevedore/tests/test_example_fields.py new file mode 100644 index 00000000..c8354e13 --- /dev/null +++ b/lib/stevedore/tests/test_example_fields.py @@ -0,0 +1,27 @@ +"""Tests for stevedore.exmaple.fields +""" + +from stevedore.example import fields + + +def test_simple_items(): + f = fields.FieldList(100) + text = ''.join(f.format({'a': 'A', 'b': 'B'})) + expected = '\n'.join([ + ': a : A', + ': b : B', + '', + ]) + assert text == expected + + +def test_long_item(): + f = fields.FieldList(25) + text = ''.join(f.format({'name': 'a value longer than the allowed width'})) + expected = '\n'.join([ + ': name : a value longer', + ' than the allowed', + ' width', + '', + ]) + assert text == expected diff --git a/lib/stevedore/tests/test_example_simple.py b/lib/stevedore/tests/test_example_simple.py new file mode 100644 index 00000000..e3758c44 --- /dev/null +++ b/lib/stevedore/tests/test_example_simple.py @@ -0,0 +1,15 @@ +"""Tests for stevedore.exmaple.simple +""" + +from stevedore.example import simple + + +def test_simple_items(): + f = simple.Simple(100) + text = ''.join(f.format({'a': 'A', 'b': 'B'})) + expected = '\n'.join([ + 'a = A', + 'b = B', + '', + ]) + assert text == expected diff --git a/lib/stevedore/tests/test_extension.py b/lib/stevedore/tests/test_extension.py new file mode 100644 index 00000000..00ba52b9 --- /dev/null +++ b/lib/stevedore/tests/test_extension.py @@ -0,0 +1,177 @@ +"""Tests for stevedore.extension +""" + +import mock + +from stevedore import extension + +ALL_NAMES = ['e1', 't1', 't2'] +WORKING_NAMES = ['t1', 't2'] + + +class FauxExtension(object): + def __init__(self, *args, **kwds): + self.args = args + self.kwds = kwds + + def get_args_and_data(self, data): + return self.args, self.kwds, data + + +class BrokenExtension(object): + def __init__(self, *args, **kwds): + raise IOError("Did not create") + + +def test_detect_plugins(): + em = extension.ExtensionManager('stevedore.test.extension') + names = sorted(em.names()) + assert names == ALL_NAMES + + +def test_get_by_name(): + em = extension.ExtensionManager('stevedore.test.extension') + e = em['t1'] + assert e.name == 't1' + + +def test_get_by_name_missing(): + em = extension.ExtensionManager('stevedore.test.extension') + try: + em['t3'] + except KeyError: + pass + else: + assert False, 'Failed to raise KeyError' + + +def test_load_multiple_times_entry_points(): + # We expect to get the same EntryPoint object because we save them + # in the cache. + em1 = extension.ExtensionManager('stevedore.test.extension') + eps1 = [ext.entry_point for ext in em1] + em2 = extension.ExtensionManager('stevedore.test.extension') + eps2 = [ext.entry_point for ext in em2] + assert eps1[0] is eps2[0] + + +def test_load_multiple_times_plugins(): + # We expect to get the same plugin object (module or class) + # because the underlying import machinery will cache the values. + em1 = extension.ExtensionManager('stevedore.test.extension') + plugins1 = [ext.plugin for ext in em1] + em2 = extension.ExtensionManager('stevedore.test.extension') + plugins2 = [ext.plugin for ext in em2] + assert plugins1[0] is plugins2[0] + + +def test_use_cache(): + # If we insert something into the cache of entry points, + # the manager should not have to call into pkg_resources + # to find the plugins. + cache = extension.ExtensionManager.ENTRY_POINT_CACHE + cache['stevedore.test.faux'] = [] + with mock.patch('pkg_resources.iter_entry_points', + side_effect=AssertionError('called iter_entry_points')): + em = extension.ExtensionManager('stevedore.test.faux') + names = em.names() + assert names == [] + + +def test_iterable(): + em = extension.ExtensionManager('stevedore.test.extension') + names = sorted(e.name for e in em) + assert names == ALL_NAMES + + +def test_invoke_on_load(): + em = extension.ExtensionManager('stevedore.test.extension', + invoke_on_load=True, + invoke_args=('a',), + invoke_kwds={'b': 'B'}, + ) + assert len(em.extensions) == 2 + for e in em.extensions: + assert e.obj.args == ('a',) + assert e.obj.kwds == {'b': 'B'} + + +def test_map_return_values(): + def mapped(ext, *args, **kwds): + return ext.name + + em = extension.ExtensionManager('stevedore.test.extension', + invoke_on_load=True, + ) + results = em.map(mapped) + assert sorted(results) == WORKING_NAMES + + +def test_map_arguments(): + objs = [] + + def mapped(ext, *args, **kwds): + objs.append((ext, args, kwds)) + + em = extension.ExtensionManager('stevedore.test.extension', + invoke_on_load=True, + ) + em.map(mapped, 1, 2, a='A', b='B') + assert len(objs) == 2 + names = sorted([o[0].name for o in objs]) + assert names == WORKING_NAMES + for o in objs: + assert o[1] == (1, 2) + assert o[2] == {'a': 'A', 'b': 'B'} + + +def test_map_eats_errors(): + + def mapped(ext, *args, **kwds): + raise RuntimeError('hard coded error') + + em = extension.ExtensionManager('stevedore.test.extension', + invoke_on_load=True, + ) + results = em.map(mapped, 1, 2, a='A', b='B') + assert results == [] + + +def test_map_propagate_exceptions(): + + def mapped(ext, *args, **kwds): + raise RuntimeError('hard coded error') + + em = extension.ExtensionManager('stevedore.test.extension', + invoke_on_load=True, + propagate_map_exceptions=True + ) + + try: + em.map(mapped, 1, 2, a='A', b='B') + assert False + except RuntimeError: + pass + + +def test_map_errors_when_no_plugins(): + + def mapped(ext, *args, **kwds): + pass + + em = extension.ExtensionManager('stevedore.test.extension.none', + invoke_on_load=True, + ) + try: + em.map(mapped, 1, 2, a='A', b='B') + except RuntimeError as err: + assert 'No stevedore.test.extension.none extensions found' == str(err) + + +def test_map_method(): + em = extension.ExtensionManager('stevedore.test.extension', + invoke_on_load=True, + ) + + result = em.map_method('get_args_and_data', 42) + assert set(r[2] for r in result) == set([42]) diff --git a/lib/stevedore/tests/test_hook.py b/lib/stevedore/tests/test_hook.py new file mode 100644 index 00000000..a6983464 --- /dev/null +++ b/lib/stevedore/tests/test_hook.py @@ -0,0 +1,43 @@ +from stevedore import hook + + +def test_hook(): + em = hook.HookManager( + 'stevedore.test.extension', + 't1', + invoke_on_load=True, + invoke_args=('a',), + invoke_kwds={'b': 'B'}, + ) + assert len(em.extensions) == 1 + assert em.names() == ['t1'] + + +def test_get_by_name(): + em = hook.HookManager( + 'stevedore.test.extension', + 't1', + invoke_on_load=True, + invoke_args=('a',), + invoke_kwds={'b': 'B'}, + ) + e_list = em['t1'] + assert len(e_list) == 1 + e = e_list[0] + assert e.name == 't1' + + +def test_get_by_name_missing(): + em = hook.HookManager( + 'stevedore.test.extension', + 't1', + invoke_on_load=True, + invoke_args=('a',), + invoke_kwds={'b': 'B'}, + ) + try: + em['t2'] + except KeyError: + pass + else: + assert False, 'Failed to raise KeyError' diff --git a/lib/stevedore/tests/test_named.py b/lib/stevedore/tests/test_named.py new file mode 100644 index 00000000..1ed9431e --- /dev/null +++ b/lib/stevedore/tests/test_named.py @@ -0,0 +1,58 @@ +from stevedore import named + +import mock + + +def test_named(): + em = named.NamedExtensionManager( + 'stevedore.test.extension', + names=['t1'], + invoke_on_load=True, + invoke_args=('a',), + invoke_kwds={'b': 'B'}, + ) + actual = em.names() + assert actual == ['t1'] + + +def test_enabled_before_load(): + # Set up the constructor for the FauxExtension to cause an + # AssertionError so the test fails if the class is instantiated, + # which should only happen if it is loaded before the name of the + # extension is compared against the names that should be loaded by + # the manager. + init_name = 'stevedore.tests.test_extension.FauxExtension.__init__' + with mock.patch(init_name) as m: + m.side_effect = AssertionError + em = named.NamedExtensionManager( + 'stevedore.test.extension', + # Look for an extension that does not exist so the + # __init__ we mocked should never be invoked. + names=['no-such-extension'], + invoke_on_load=True, + invoke_args=('a',), + invoke_kwds={'b': 'B'}, + ) + actual = em.names() + assert actual == [] + + +def test_extensions_listed_in_name_order(): + # Since we don't know the "natural" order of the extensions, run + # the test both ways: if the sorting is broken, one of them will + # fail + em = named.NamedExtensionManager( + 'stevedore.test.extension', + names=['t1', 't2'], + name_order=True + ) + actual = em.names() + assert actual == ['t1', 't2'] + + em = named.NamedExtensionManager( + 'stevedore.test.extension', + names=['t2', 't1'], + name_order=True + ) + actual = em.names() + assert actual == ['t2', 't1'] diff --git a/lib/stevedore/tests/test_test_manager.py b/lib/stevedore/tests/test_test_manager.py new file mode 100644 index 00000000..aa995b25 --- /dev/null +++ b/lib/stevedore/tests/test_test_manager.py @@ -0,0 +1,278 @@ +from mock import Mock, sentinel +from nose.tools import raises +from stevedore import (ExtensionManager, NamedExtensionManager, HookManager, + DriverManager, EnabledExtensionManager) +from stevedore.dispatch import (DispatchExtensionManager, + NameDispatchExtensionManager) +from stevedore.extension import Extension + + +test_extension = Extension('test_extension', None, None, None) +test_extension2 = Extension('another_one', None, None, None) + +mock_entry_point = Mock(module_name='test.extension', attrs=['obj']) +a_driver = Extension('test_driver', mock_entry_point, sentinel.driver_plugin, + sentinel.driver_obj) + + +# base ExtensionManager + +def test_instance_should_use_supplied_extensions(): + extensions = [test_extension, test_extension2] + em = ExtensionManager.make_test_instance(extensions) + + assert extensions == em.extensions + + +def test_instance_should_have_default_namespace(): + em = ExtensionManager.make_test_instance([]) + + assert em.namespace + + +def test_instance_should_use_supplied_namespace(): + namespace = 'testing.1.2.3' + em = ExtensionManager.make_test_instance([], namespace=namespace) + + assert namespace == em.namespace + + +def test_extension_name_should_be_listed(): + em = ExtensionManager.make_test_instance([test_extension]) + + assert test_extension.name in em.names() + + +def test_iterator_should_yield_extension(): + em = ExtensionManager.make_test_instance([test_extension]) + + assert test_extension == next(iter(em)) + + +def test_manager_should_allow_name_access(): + em = ExtensionManager.make_test_instance([test_extension]) + + assert test_extension == em[test_extension.name] + + +def test_manager_should_call(): + em = ExtensionManager.make_test_instance([test_extension]) + func = Mock() + + em.map(func) + + func.assert_called_once_with(test_extension) + + +def test_manager_should_call_all(): + em = ExtensionManager.make_test_instance([test_extension2, + test_extension]) + func = Mock() + + em.map(func) + + func.assert_any_call(test_extension2) + func.assert_any_call(test_extension) + + +def test_manager_return_values(): + def mapped(ext, *args, **kwds): + return ext.name + + em = ExtensionManager.make_test_instance([test_extension2, + test_extension]) + results = em.map(mapped) + assert sorted(results) == ['another_one', 'test_extension'] + + +def test_manager_should_eat_exceptions(): + em = ExtensionManager.make_test_instance([test_extension]) + + func = Mock(side_effect=RuntimeError('hard coded error')) + + results = em.map(func, 1, 2, a='A', b='B') + assert results == [] + + +@raises(RuntimeError) +def test_manager_should_propagate_exceptions(): + em = ExtensionManager.make_test_instance([test_extension], + propagate_map_exceptions=True) + func = Mock(side_effect=RuntimeError('hard coded error')) + + em.map(func, 1, 2, a='A', b='B') + + +# NamedExtensionManager + +def test_named_manager_should_use_supplied_extensions(): + extensions = [test_extension, test_extension2] + em = NamedExtensionManager.make_test_instance(extensions) + + assert extensions == em.extensions + + +def test_named_manager_should_have_default_namespace(): + em = NamedExtensionManager.make_test_instance([]) + + assert em.namespace + + +def test_named_manager_should_use_supplied_namespace(): + namespace = 'testing.1.2.3' + em = NamedExtensionManager.make_test_instance([], namespace=namespace) + + assert namespace == em.namespace + + +def test_named_manager_should_populate_names(): + extensions = [test_extension, test_extension2] + em = NamedExtensionManager.make_test_instance(extensions) + + assert ['test_extension', 'another_one'] == em.names() + + +# HookManager + +def test_hook_manager_should_use_supplied_extensions(): + extensions = [test_extension, test_extension2] + em = HookManager.make_test_instance(extensions) + + assert extensions == em.extensions + + +def test_hook_manager_should_be_first_extension_name(): + extensions = [test_extension, test_extension2] + em = HookManager.make_test_instance(extensions) + + # This will raise KeyError if the names don't match + assert em[test_extension.name] + + +def test_hook_manager_should_have_default_namespace(): + em = HookManager.make_test_instance([test_extension]) + + assert em.namespace + + +def test_hook_manager_should_use_supplied_namespace(): + namespace = 'testing.1.2.3' + em = HookManager.make_test_instance([test_extension], namespace=namespace) + + assert namespace == em.namespace + + +def test_hook_manager_should_return_named_extensions(): + hook1 = Extension('captain', None, None, None) + hook2 = Extension('captain', None, None, None) + + em = HookManager.make_test_instance([hook1, hook2]) + + assert [hook1, hook2] == em['captain'] + + +# DriverManager + +def test_driver_manager_should_use_supplied_extension(): + em = DriverManager.make_test_instance(a_driver) + + assert [a_driver] == em.extensions + + +def test_driver_manager_should_have_default_namespace(): + em = DriverManager.make_test_instance(a_driver) + + assert em.namespace + + +def test_driver_manager_should_use_supplied_namespace(): + namespace = 'testing.1.2.3' + em = DriverManager.make_test_instance(a_driver, namespace=namespace) + + assert namespace == em.namespace + + +def test_instance_should_use_driver_name(): + em = DriverManager.make_test_instance(a_driver) + + assert ['test_driver'] == em.names() + + +def test_instance_call(): + def invoke(ext, *args, **kwds): + return ext.name, args, kwds + + em = DriverManager.make_test_instance(a_driver) + result = em(invoke, 'a', b='C') + + assert result == ('test_driver', ('a',), {'b': 'C'}) + + +def test_instance_driver_property(): + em = DriverManager.make_test_instance(a_driver) + + assert sentinel.driver_obj == em.driver + + +# EnabledExtensionManager + +def test_enabled_instance_should_use_supplied_extensions(): + extensions = [test_extension, test_extension2] + em = EnabledExtensionManager.make_test_instance(extensions) + + assert extensions == em.extensions + + +# DispatchExtensionManager + +def test_dispatch_instance_should_use_supplied_extensions(): + extensions = [test_extension, test_extension2] + em = DispatchExtensionManager.make_test_instance(extensions) + + assert extensions == em.extensions + + +def test_dispatch_map_should_invoke_filter_for_extensions(): + em = DispatchExtensionManager.make_test_instance([test_extension, + test_extension2]) + + filter_func = Mock(return_value=False) + + args = ('A',) + kw = {'big': 'Cheese'} + + em.map(filter_func, None, *args, **kw) + + filter_func.assert_any_call(test_extension, *args, **kw) + filter_func.assert_any_call(test_extension2, *args, **kw) + + +# NameDispatchExtensionManager + +def test_name_dispatch_instance_should_use_supplied_extensions(): + extensions = [test_extension, test_extension2] + em = NameDispatchExtensionManager.make_test_instance(extensions) + + assert extensions == em.extensions + + +def test_name_dispatch_instance_should_build_extension_name_map(): + extensions = [test_extension, test_extension2] + em = NameDispatchExtensionManager.make_test_instance(extensions) + + assert test_extension == em.by_name[test_extension.name] + assert test_extension2 == em.by_name[test_extension2.name] + + +def test_named_dispatch_map_should_invoke_filter_for_extensions(): + em = NameDispatchExtensionManager.make_test_instance([test_extension, + test_extension2]) + + func = Mock() + + args = ('A',) + kw = {'BIGGER': 'Cheese'} + + em.map(['test_extension'], func, *args, **kw) + + func.assert_called_once_with(test_extension, *args, **kw) diff --git a/nzbtomedia/__init__.py b/nzbtomedia/__init__.py index 8f396728..ea8ab32f 100644 --- a/nzbtomedia/__init__.py +++ b/nzbtomedia/__init__.py @@ -84,6 +84,7 @@ DELUGEPORT = None DELUGEUSR = None DELUGEPWD = None +EXTCONTAINER = None COMPRESSEDCONTAINER = None MEDIACONTAINER = None AUDIOCONTAINER = None @@ -136,7 +137,7 @@ def initialize(section=None): TRANSCODE, GIT_PATH, GIT_USER, GIT_BRANCH, GIT_REPO, SYS_ENCODING, NZB_CLIENTAGENT, SABNZBDHOST, SABNZBDPORT, SABNZBDAPIKEY, \ DUPLICATE, IGNOREEXTENSIONS, OUTPUTVIDEOEXTENSION, OUTPUTVIDEOCODEC, OUTPUTVIDEOPRESET, OUTPUTVIDEOFRAMERATE, \ OUTPUTVIDEOBITRATE, OUTPUTAUDIOCODEC, OUTPUTAUDIOBITRATE, OUTPUTSUBTITLECODEC, OUTPUTFASTSTART, OUTPUTQUALITYPERCENT, \ - NICENESS, LOG_DEBUG, FORCE_CLEAN, FFMPEG_PATH, FFMPEG, FFPROBE, AUDIOCONTAINER + NICENESS, LOG_DEBUG, FORCE_CLEAN, FFMPEG_PATH, FFMPEG, FFPROBE, AUDIOCONTAINER, EXTCONTAINER if __INITIALIZED__: return False @@ -240,12 +241,19 @@ def initialize(section=None): DELUGEUSR = CFG["Torrent"]["DelugeUSR"] # mysecretusr DELUGEPWD = CFG["Torrent"]["DelugePWD"] # mysecretpwr - COMPRESSEDCONTAINER = (CFG["Extensions"]["compressedExtensions"]) # .zip,.rar,.7z - MEDIACONTAINER = (CFG["Extensions"]["mediaExtensions"]) # .mkv,.avi,.divx - AUDIOCONTAINER = (CFG["Extensions"]["audioExtensions"]) - METACONTAINER = (CFG["Extensions"]["metaExtensions"]) # .nfo,.sub,.srt + COMPRESSEDCONTAINER = CFG["Extensions"]["compressedExtensions"] + MEDIACONTAINER = CFG["Extensions"]["mediaExtensions"] + AUDIOCONTAINER = CFG["Extensions"]["audioExtensions"] + METACONTAINER = CFG["Extensions"]["metaExtensions"] # .nfo,.sub,.srt + if not isinstance(COMPRESSEDCONTAINER, list):COMPRESSEDCONTAINER = [COMPRESSEDCONTAINER] + if not isinstance(MEDIACONTAINER, list): MEDIACONTAINER = [MEDIACONTAINER] + if not isinstance(AUDIOCONTAINER, list): AUDIOCONTAINER = [AUDIOCONTAINER] + if not isinstance(METACONTAINER, list): METACONTAINER = [METACONTAINER] + + EXTCONTAINER = [y for x in COMPRESSEDCONTAINER,MEDIACONTAINER,AUDIOCONTAINER,METACONTAINER for y in x] + MINSAMPLESIZE = int(CFG["Extensions"]["minSampleSize"]) # 200 (in MB) - SAMPLEIDS = (CFG["Extensions"]["SampleIDs"]) # sample,-s. + SAMPLEIDS = CFG["Extensions"]["SampleIDs"] TRANSCODE = int(CFG["Transcoder"]["transcode"]) DUPLICATE = int(CFG["Transcoder"]["duplicate"]) diff --git a/nzbtomedia/nzbToMediaUtil.py b/nzbtomedia/nzbToMediaUtil.py index 3e87d11d..ad340eca 100644 --- a/nzbtomedia/nzbToMediaUtil.py +++ b/nzbtomedia/nzbToMediaUtil.py @@ -581,19 +581,18 @@ def clean_nzbname(nzbname): nzbname = re.sub("^\[.*\]", "", nzbname) return nzbname.strip() -def isMediaFile(filename): +def isMediaFile(mediafile): + fileName, fileExt = os.path.splitext(mediafile) + # ignore MAC OS's retarded "resource fork" files - if filename.startswith('._'): + if fileName.startswith('._'): return False - sepFile = filename.rpartition(".") - - if re.search('extras?$', sepFile[0], re.I): - logger.info("Ignoring extras file: %s " % (filename)) + if re.search('extras?$', fileName, re.I): + logger.info("Ignoring extras file: %s " % (mediafile)) return False - if sepFile[2].lower() in [i.replace(".","") for i in - (nzbtomedia.MEDIACONTAINER + nzbtomedia.AUDIOCONTAINER + nzbtomedia.COMPRESSEDCONTAINER)]: + if fileExt.lower() in nzbtomedia.EXTCONTAINER: return True else: return False diff --git a/tests/general.py b/tests/general.py index 4126436b..2f41e850 100644 --- a/tests/general.py +++ b/tests/general.py @@ -19,6 +19,23 @@ copy_list = [] # Initialize the config nzbtomedia.initialize() +EXTCONTAINER = [] + +try: + EXTCONTAINER += nzbtomedia.COMPRESSEDCONTAINER.split(',') +except: + EXTCONTAINER.extend(nzbtomedia.COMPRESSEDCONTAINER) + +try: + EXTCONTAINER += nzbtomedia.MEDIACONTAINER.split(',') +except: + EXTCONTAINER.extend(nzbtomedia.MEDIACONTAINER) + +try: + EXTCONTAINER += nzbtomedia.AUDIOCONTAINER.split(',') +except: + EXTCONTAINER.extend(nzbtomedia.AUDIOCONTAINER) + inputDirectory = "Z:\complete\movie\The.Lego.Movie.2014.R5.x264.English.XviD-vTg.nfo_0166_-_The.Lego.Movie.2014.R5.x264.English.XviD-vTg.nfo_yEn.cp(tt1490017)" inputName = "The.Lego.Movie.2014.R5.x264.English.XviD-vTg.nfo_0166_-_The.Lego.Movie.2014.R5.x264.English.XviD-vTg.nfo_yEn.cp(tt1490017)" inputCategory = 'movie'