From ce1c2b6b05c08b669357947e61ae40efce7fc50f Mon Sep 17 00:00:00 2001 From: Walter Purcaro Date: Mon, 16 Feb 2015 10:46:28 +0100 Subject: module temp --- module/plugins/Account.py | 307 +++++++++ module/plugins/Addon.py | 185 ++++++ module/plugins/Captcha.py | 51 ++ module/plugins/Container.py | 66 ++ module/plugins/Crypter.py | 107 +++ module/plugins/Hoster.py | 21 + module/plugins/OCR.py | 315 +++++++++ module/plugins/Plugin.py | 753 ++++++++++++++++++++++ module/plugins/__init__.py | 1 + module/plugins/accounts/AlldebridCom.py | 59 ++ module/plugins/accounts/BayfilesCom.py | 37 ++ module/plugins/accounts/BillionuploadsCom.py | 16 + module/plugins/accounts/BitshareCom.py | 32 + module/plugins/accounts/CatShareNet.py | 56 ++ module/plugins/accounts/CramitIn.py | 16 + module/plugins/accounts/CzshareCom.py | 44 ++ module/plugins/accounts/DebridItaliaCom.py | 44 ++ module/plugins/accounts/DepositfilesCom.py | 35 + module/plugins/accounts/DropboxCom.py | 42 ++ module/plugins/accounts/EasybytezCom.py | 19 + module/plugins/accounts/EuroshareEu.py | 41 ++ module/plugins/accounts/FastixRu.py | 38 ++ module/plugins/accounts/FastshareCz.py | 53 ++ module/plugins/accounts/File4SafeCom.py | 18 + module/plugins/accounts/FileParadoxIn.py | 16 + module/plugins/accounts/FilecloudIo.py | 59 ++ module/plugins/accounts/FilefactoryCom.py | 49 ++ module/plugins/accounts/FilejungleCom.py | 49 ++ module/plugins/accounts/FileomCom.py | 16 + module/plugins/accounts/FilerNet.py | 50 ++ module/plugins/accounts/FilerioCom.py | 16 + module/plugins/accounts/FilesMailRu.py | 28 + module/plugins/accounts/FileserveCom.py | 44 ++ module/plugins/accounts/FourSharedCom.py | 33 + module/plugins/accounts/FreakshareCom.py | 43 ++ module/plugins/accounts/FreeWayMe.py | 55 ++ module/plugins/accounts/FshareVn.py | 63 ++ module/plugins/accounts/Ftp.py | 17 + module/plugins/accounts/HellshareCz.py | 76 +++ module/plugins/accounts/Http.py | 17 + module/plugins/accounts/HugefilesNet.py | 16 + module/plugins/accounts/HundredEightyUploadCom.py | 16 + module/plugins/accounts/JunocloudMe.py | 16 + module/plugins/accounts/Keep2ShareCc.py | 69 ++ module/plugins/accounts/LetitbitNet.py | 34 + module/plugins/accounts/LinestorageCom.py | 16 + module/plugins/accounts/LinksnappyCom.py | 50 ++ module/plugins/accounts/LomafileCom.py | 16 + module/plugins/accounts/MegaDebridEu.py | 39 ++ module/plugins/accounts/MegaRapidCz.py | 59 ++ module/plugins/accounts/MegasharesCom.py | 48 ++ module/plugins/accounts/MovReelCom.py | 19 + module/plugins/accounts/MultishareCz.py | 44 ++ module/plugins/accounts/MyfastfileCom.py | 35 + module/plugins/accounts/NetloadIn.py | 40 ++ module/plugins/accounts/NosuploadCom.py | 16 + module/plugins/accounts/NovafileCom.py | 16 + module/plugins/accounts/NowVideoSx.py | 56 ++ module/plugins/accounts/OboomCom.py | 62 ++ module/plugins/accounts/OneFichierCom.py | 55 ++ module/plugins/accounts/OverLoadMe.py | 36 ++ module/plugins/accounts/PremiumTo.py | 34 + module/plugins/accounts/PremiumizeMe.py | 49 ++ module/plugins/accounts/QuickshareCz.py | 43 ++ module/plugins/accounts/RPNetBiz.py | 51 ++ module/plugins/accounts/RapidfileshareNet.py | 18 + module/plugins/accounts/RapidgatorNet.py | 58 ++ module/plugins/accounts/RapiduNet.py | 48 ++ module/plugins/accounts/RarefileNet.py | 16 + module/plugins/accounts/RealdebridCom.py | 36 ++ module/plugins/accounts/RehostTo.py | 41 ++ module/plugins/accounts/RyushareCom.py | 25 + module/plugins/accounts/SafesharingEu.py | 16 + module/plugins/accounts/SecureUploadEu.py | 16 + module/plugins/accounts/SendmywayCom.py | 16 + module/plugins/accounts/ShareonlineBiz.py | 45 ++ module/plugins/accounts/SimplyPremiumCom.py | 46 ++ module/plugins/accounts/SimplydebridCom.py | 34 + module/plugins/accounts/StahnuTo.py | 34 + module/plugins/accounts/StreamcloudEu.py | 16 + module/plugins/accounts/TurbobitNet.py | 42 ++ module/plugins/accounts/TusfilesNet.py | 23 + module/plugins/accounts/UlozTo.py | 52 ++ module/plugins/accounts/UnrestrictLi.py | 44 ++ module/plugins/accounts/UploadcCom.py | 16 + module/plugins/accounts/UploadedTo.py | 60 ++ module/plugins/accounts/UploadheroCom.py | 41 ++ module/plugins/accounts/UploadingCom.py | 63 ++ module/plugins/accounts/UptoboxCom.py | 17 + module/plugins/accounts/VidPlayNet.py | 16 + module/plugins/accounts/XFileSharingPro.py | 30 + module/plugins/accounts/YibaishiwuCom.py | 40 ++ module/plugins/accounts/ZeveraCom.py | 56 ++ module/plugins/accounts/__init__.py | 1 + module/plugins/addon/Checksum.py | 186 ++++++ module/plugins/addon/ClickAndLoad.py | 74 +++ module/plugins/addon/DeleteFinished.py | 79 +++ module/plugins/addon/DownloadScheduler.py | 77 +++ module/plugins/addon/ExternalScripts.py | 145 +++++ module/plugins/addon/ExtractArchive.py | 363 +++++++++++ module/plugins/addon/HotFolder.py | 70 ++ module/plugins/addon/IRCInterface.py | 431 +++++++++++++ module/plugins/addon/MergeFiles.py | 85 +++ module/plugins/addon/MultiHome.py | 81 +++ module/plugins/addon/RestartFailed.py | 45 ++ module/plugins/addon/RestartSlow.py | 57 ++ module/plugins/addon/SkipRev.py | 77 +++ module/plugins/addon/UnSkipOnFail.py | 87 +++ module/plugins/addon/UpdateManager.py | 305 +++++++++ module/plugins/addon/WindowsPhoneToastNotify.py | 57 ++ module/plugins/addon/XMPPInterface.py | 252 ++++++++ module/plugins/addon/__init__.py | 1 + module/plugins/captcha/AdYouLike.py | 107 +++ module/plugins/captcha/AdsCaptcha.py | 77 +++ module/plugins/captcha/ReCaptcha.py | 73 +++ module/plugins/captcha/SolveMedia.py | 50 ++ module/plugins/captcha/__init__.py | 1 + module/plugins/container/CCF.py | 43 ++ module/plugins/container/LinkList.py | 71 ++ module/plugins/container/RSDF.py | 56 ++ module/plugins/container/__init__.py | 1 + module/plugins/crypter/BitshareCom.py | 21 + module/plugins/crypter/C1NeonCom.py | 19 + module/plugins/crypter/ChipDe.py | 29 + module/plugins/crypter/CrockoCom.py | 20 + module/plugins/crypter/CryptItCom.py | 19 + module/plugins/crypter/CzshareCom.py | 32 + module/plugins/crypter/DDLMusicOrg.py | 51 ++ module/plugins/crypter/DailymotionBatch.py | 106 +++ module/plugins/crypter/DataHu.py | 40 ++ module/plugins/crypter/DdlstorageCom.py | 20 + module/plugins/crypter/DepositfilesCom.py | 20 + module/plugins/crypter/Dereferer.py | 26 + module/plugins/crypter/DevhostStFolder.py | 58 ++ module/plugins/crypter/DlProtectCom.py | 65 ++ module/plugins/crypter/DontKnowMe.py | 29 + module/plugins/crypter/DuckCryptInfo.py | 59 ++ module/plugins/crypter/DuploadOrg.py | 19 + module/plugins/crypter/EasybytezCom.py | 22 + module/plugins/crypter/EmbeduploadCom.py | 60 ++ module/plugins/crypter/FilebeerInfo.py | 19 + module/plugins/crypter/FilecloudIo.py | 21 + module/plugins/crypter/FilecryptCc.py | 148 +++++ module/plugins/crypter/FilefactoryCom.py | 28 + module/plugins/crypter/FilerNet.py | 26 + module/plugins/crypter/FileserveCom.py | 38 ++ module/plugins/crypter/FilesonicCom.py | 18 + module/plugins/crypter/FilestubeCom.py | 21 + module/plugins/crypter/FiletramCom.py | 22 + module/plugins/crypter/FiredriveCom.py | 19 + module/plugins/crypter/FourChanOrg.py | 27 + module/plugins/crypter/FreakhareCom.py | 38 ++ module/plugins/crypter/FreetexthostCom.py | 27 + module/plugins/crypter/FshareVn.py | 20 + module/plugins/crypter/Go4UpCom.py | 49 ++ module/plugins/crypter/GooGl.py | 32 + module/plugins/crypter/HoerbuchIn.py | 62 ++ module/plugins/crypter/HotfileCom.py | 19 + module/plugins/crypter/ILoadTo.py | 19 + module/plugins/crypter/ImgurComAlbum.py | 27 + module/plugins/crypter/JunocloudMe.py | 20 + module/plugins/crypter/LetitbitNet.py | 33 + module/plugins/crypter/LinkCryptWs.py | 327 ++++++++++ module/plugins/crypter/LinkSaveIn.py | 246 +++++++ module/plugins/crypter/LinkdecrypterCom.py | 92 +++ module/plugins/crypter/LixIn.py | 62 ++ module/plugins/crypter/LofCc.py | 19 + module/plugins/crypter/MBLinkInfo.py | 20 + module/plugins/crypter/MediafireCom.py | 58 ++ module/plugins/crypter/MegaRapidCz.py | 20 + module/plugins/crypter/MegauploadCom.py | 18 + module/plugins/crypter/Movie2KTo.py | 19 + module/plugins/crypter/MultiUpOrg.py | 38 ++ module/plugins/crypter/MultiloadCz.py | 42 ++ module/plugins/crypter/MultiuploadCom.py | 18 + module/plugins/crypter/NCryptIn.py | 315 +++++++++ module/plugins/crypter/NetfolderIn.py | 70 ++ module/plugins/crypter/NosvideoCom.py | 21 + module/plugins/crypter/OneKhDe.py | 40 ++ module/plugins/crypter/OronCom.py | 19 + module/plugins/crypter/PastebinCom.py | 21 + module/plugins/crypter/QuickshareCz.py | 31 + module/plugins/crypter/RSLayerCom.py | 19 + module/plugins/crypter/RapidfileshareNet.py | 20 + module/plugins/crypter/RelinkUs.py | 293 +++++++++ module/plugins/crypter/SafelinkingNet.py | 79 +++ module/plugins/crypter/SecuredIn.py | 19 + module/plugins/crypter/SexuriaCom.py | 94 +++ module/plugins/crypter/ShareLinksBiz.py | 286 ++++++++ module/plugins/crypter/SharingmatrixCom.py | 18 + module/plugins/crypter/SpeedLoadOrg.py | 19 + module/plugins/crypter/StealthTo.py | 19 + module/plugins/crypter/TnyCz.py | 27 + module/plugins/crypter/TrailerzoneInfo.py | 19 + module/plugins/crypter/TurbobitNet.py | 44 ++ module/plugins/crypter/TusfilesNet.py | 45 ++ module/plugins/crypter/UlozTo.py | 46 ++ module/plugins/crypter/UploadableCh.py | 24 + module/plugins/crypter/UploadedTo.py | 34 + module/plugins/crypter/WiiReloadedOrg.py | 19 + module/plugins/crypter/WuploadCom.py | 18 + module/plugins/crypter/XFileSharingPro.py | 47 ++ module/plugins/crypter/XupPl.py | 25 + module/plugins/crypter/YoutubeBatch.py | 148 +++++ module/plugins/crypter/__init__.py | 1 + module/plugins/hooks/AlldebridCom.py | 27 + module/plugins/hooks/BypassCaptcha.py | 133 ++++ module/plugins/hooks/Captcha9Kw.py | 253 ++++++++ module/plugins/hooks/CaptchaBrotherhood.py | 166 +++++ module/plugins/hooks/DeathByCaptcha.py | 213 ++++++ module/plugins/hooks/DebridItaliaCom.py | 27 + module/plugins/hooks/EasybytezCom.py | 39 ++ module/plugins/hooks/ExpertDecoders.py | 92 +++ module/plugins/hooks/FastixRu.py | 28 + module/plugins/hooks/FreeWayMe.py | 25 + module/plugins/hooks/ImageTyperz.py | 151 +++++ module/plugins/hooks/LinkdecrypterCom.py | 60 ++ module/plugins/hooks/LinksnappyCom.py | 27 + module/plugins/hooks/MegaDebridEu.py | 30 + module/plugins/hooks/MultishareCz.py | 27 + module/plugins/hooks/MyfastfileCom.py | 30 + module/plugins/hooks/OverLoadMe.py | 29 + module/plugins/hooks/PremiumTo.py | 38 ++ module/plugins/hooks/PremiumizeMe.py | 54 ++ module/plugins/hooks/RPNetBiz.py | 52 ++ module/plugins/hooks/RealdebridCom.py | 27 + module/plugins/hooks/RehostTo.py | 41 ++ module/plugins/hooks/SimplyPremiumCom.py | 29 + module/plugins/hooks/SimplydebridCom.py | 22 + module/plugins/hooks/UnrestrictLi.py | 30 + module/plugins/hooks/XFileSharingPro.py | 96 +++ module/plugins/hooks/ZeveraCom.py | 22 + module/plugins/hooks/__init__.py | 1 + module/plugins/hoster/AlldebridCom.py | 87 +++ module/plugins/hoster/BayfilesCom.py | 87 +++ module/plugins/hoster/BezvadataCz.py | 94 +++ module/plugins/hoster/BillionuploadsCom.py | 24 + module/plugins/hoster/BitshareCom.py | 157 +++++ module/plugins/hoster/BoltsharingCom.py | 18 + module/plugins/hoster/CatShareNet.py | 67 ++ module/plugins/hoster/CloudzerNet.py | 20 + module/plugins/hoster/CramitIn.py | 24 + module/plugins/hoster/CrockoCom.py | 70 ++ module/plugins/hoster/CyberlockerCh.py | 18 + module/plugins/hoster/CzshareCom.py | 152 +++++ module/plugins/hoster/DailymotionCom.py | 125 ++++ module/plugins/hoster/DataHu.py | 42 ++ module/plugins/hoster/DataportCz.py | 55 ++ module/plugins/hoster/DateiTo.py | 82 +++ module/plugins/hoster/DdlstorageCom.py | 19 + module/plugins/hoster/DebridItaliaCom.py | 53 ++ module/plugins/hoster/DepositfilesCom.py | 123 ++++ module/plugins/hoster/DevhostSt.py | 48 ++ module/plugins/hoster/DlFreeFr.py | 136 ++++ module/plugins/hoster/DodanePl.py | 18 + module/plugins/hoster/DuploadOrg.py | 18 + module/plugins/hoster/EasybytezCom.py | 26 + module/plugins/hoster/EdiskCz.py | 56 ++ module/plugins/hoster/EgoFilesCom.py | 18 + module/plugins/hoster/EnteruploadCom.py | 18 + module/plugins/hoster/EpicShareNet.py | 18 + module/plugins/hoster/EuroshareEu.py | 67 ++ module/plugins/hoster/ExtabitCom.py | 79 +++ module/plugins/hoster/FastixRu.py | 76 +++ module/plugins/hoster/FastshareCz.py | 77 +++ module/plugins/hoster/FileApeCom.py | 18 + module/plugins/hoster/FileParadoxIn.py | 25 + module/plugins/hoster/FileSharkPl.py | 138 ++++ module/plugins/hoster/FileStoreTo.py | 37 ++ module/plugins/hoster/FilebeerInfo.py | 18 + module/plugins/hoster/FilecloudIo.py | 125 ++++ module/plugins/hoster/FilefactoryCom.py | 90 +++ module/plugins/hoster/FilejungleCom.py | 29 + module/plugins/hoster/FileomCom.py | 35 + module/plugins/hoster/FilepostCom.py | 130 ++++ module/plugins/hoster/FilepupNet.py | 51 ++ module/plugins/hoster/FilerNet.py | 80 +++ module/plugins/hoster/FilerioCom.py | 25 + module/plugins/hoster/FilesMailRu.py | 106 +++ module/plugins/hoster/FileserveCom.py | 217 +++++++ module/plugins/hoster/FileshareInUa.py | 18 + module/plugins/hoster/FilesonicCom.py | 19 + module/plugins/hoster/FilezyNet.py | 18 + module/plugins/hoster/FiredriveCom.py | 18 + module/plugins/hoster/FlyFilesNet.py | 45 ++ module/plugins/hoster/FourSharedCom.py | 61 ++ module/plugins/hoster/FreakshareCom.py | 176 +++++ module/plugins/hoster/FreeWayMe.py | 36 ++ module/plugins/hoster/FreevideoCz.py | 18 + module/plugins/hoster/FshareVn.py | 125 ++++ module/plugins/hoster/Ftp.py | 79 +++ module/plugins/hoster/GamefrontCom.py | 90 +++ module/plugins/hoster/GigapetaCom.py | 64 ++ module/plugins/hoster/GooIm.py | 39 ++ module/plugins/hoster/HellshareCz.py | 48 ++ module/plugins/hoster/HellspyCz.py | 18 + module/plugins/hoster/HotfileCom.py | 21 + module/plugins/hoster/HugefilesNet.py | 27 + module/plugins/hoster/HundredEightyUploadCom.py | 27 + module/plugins/hoster/IFileWs.py | 18 + module/plugins/hoster/IcyFilesCom.py | 18 + module/plugins/hoster/IfileIt.py | 67 ++ module/plugins/hoster/IfolderRu.py | 76 +++ module/plugins/hoster/JumbofilesCom.py | 38 ++ module/plugins/hoster/JunocloudMe.py | 28 + module/plugins/hoster/Keep2ShareCc.py | 132 ++++ module/plugins/hoster/KickloadCom.py | 18 + module/plugins/hoster/KingfilesNet.py | 82 +++ module/plugins/hoster/LemUploadsCom.py | 18 + module/plugins/hoster/LetitbitNet.py | 142 ++++ module/plugins/hoster/LinksnappyCom.py | 76 +++ module/plugins/hoster/LoadTo.py | 75 +++ module/plugins/hoster/LomafileCom.py | 30 + module/plugins/hoster/LuckyShareNet.py | 73 +++ module/plugins/hoster/MediafireCom.py | 124 ++++ module/plugins/hoster/MegaCoNz.py | 171 +++++ module/plugins/hoster/MegaDebridEu.py | 94 +++ module/plugins/hoster/MegaFilesSe.py | 18 + module/plugins/hoster/MegaRapidCz.py | 71 ++ module/plugins/hoster/MegacrypterCom.py | 56 ++ module/plugins/hoster/MegareleaseOrg.py | 19 + module/plugins/hoster/MegasharesCom.py | 113 ++++ module/plugins/hoster/MegauploadCom.py | 18 + module/plugins/hoster/MegavideoCom.py | 19 + module/plugins/hoster/MovReelCom.py | 26 + module/plugins/hoster/MultishareCz.py | 80 +++ module/plugins/hoster/MyfastfileCom.py | 47 ++ module/plugins/hoster/MyvideoDe.py | 49 ++ module/plugins/hoster/NahrajCz.py | 18 + module/plugins/hoster/NarodRu.py | 60 ++ module/plugins/hoster/NetloadIn.py | 294 +++++++++ module/plugins/hoster/NosuploadCom.py | 43 ++ module/plugins/hoster/NovafileCom.py | 31 + module/plugins/hoster/NowDownloadSx.py | 64 ++ module/plugins/hoster/NowVideoSx.py | 44 ++ module/plugins/hoster/OboomCom.py | 145 +++++ module/plugins/hoster/OneFichierCom.py | 71 ++ module/plugins/hoster/OronCom.py | 19 + module/plugins/hoster/OverLoadMe.py | 84 +++ module/plugins/hoster/PandaplaNet.py | 18 + module/plugins/hoster/PornhostCom.py | 80 +++ module/plugins/hoster/PornhubCom.py | 89 +++ module/plugins/hoster/PotloadCom.py | 18 + module/plugins/hoster/PremiumTo.py | 81 +++ module/plugins/hoster/PremiumizeMe.py | 56 ++ module/plugins/hoster/PromptfileCom.py | 45 ++ module/plugins/hoster/PrzeklejPl.py | 18 + module/plugins/hoster/QuickshareCz.py | 90 +++ module/plugins/hoster/RPNetBiz.py | 85 +++ module/plugins/hoster/RapidfileshareNet.py | 31 + module/plugins/hoster/RapidgatorNet.py | 199 ++++++ module/plugins/hoster/RapiduNet.py | 82 +++ module/plugins/hoster/RarefileNet.py | 28 + module/plugins/hoster/RealdebridCom.py | 94 +++ module/plugins/hoster/RedtubeCom.py | 62 ++ module/plugins/hoster/RehostTo.py | 44 ++ module/plugins/hoster/RemixshareCom.py | 61 ++ module/plugins/hoster/RgHostNet.py | 26 + module/plugins/hoster/RyushareCom.py | 81 +++ module/plugins/hoster/SafesharingEu.py | 25 + module/plugins/hoster/SecureUploadEu.py | 23 + module/plugins/hoster/SendmywayCom.py | 24 + module/plugins/hoster/SendspaceCom.py | 60 ++ module/plugins/hoster/Share4WebCom.py | 22 + module/plugins/hoster/Share76Com.py | 18 + module/plugins/hoster/ShareFilesCo.py | 18 + module/plugins/hoster/SharebeesCom.py | 18 + module/plugins/hoster/ShareonlineBiz.py | 191 ++++++ module/plugins/hoster/ShareplaceCom.py | 89 +++ module/plugins/hoster/SharingmatrixCom.py | 19 + module/plugins/hoster/ShragleCom.py | 19 + module/plugins/hoster/SimplyPremiumCom.py | 82 +++ module/plugins/hoster/SimplydebridCom.py | 64 ++ module/plugins/hoster/SockshareCom.py | 20 + module/plugins/hoster/SoundcloudCom.py | 57 ++ module/plugins/hoster/SpeedLoadOrg.py | 18 + module/plugins/hoster/SpeedfileCz.py | 18 + module/plugins/hoster/SpeedyshareCom.py | 51 ++ module/plugins/hoster/StorageTo.py | 18 + module/plugins/hoster/StreamCz.py | 71 ++ module/plugins/hoster/StreamcloudEu.py | 31 + module/plugins/hoster/TurbobitNet.py | 173 +++++ module/plugins/hoster/TurbouploadCom.py | 18 + module/plugins/hoster/TusfilesNet.py | 35 + module/plugins/hoster/TwoSharedCom.py | 41 ++ module/plugins/hoster/UlozTo.py | 164 +++++ module/plugins/hoster/UloziskoSk.py | 72 +++ module/plugins/hoster/UnibytesCom.py | 70 ++ module/plugins/hoster/UnrestrictLi.py | 91 +++ module/plugins/hoster/UpleaCom.py | 60 ++ module/plugins/hoster/UploadStationCom.py | 19 + module/plugins/hoster/UploadableCh.py | 90 +++ module/plugins/hoster/UploadboxCom.py | 18 + module/plugins/hoster/UploadedTo.py | 245 +++++++ module/plugins/hoster/UploadhereCom.py | 18 + module/plugins/hoster/UploadheroCom.py | 81 +++ module/plugins/hoster/UploadingCom.py | 104 +++ module/plugins/hoster/UploadkingCom.py | 18 + module/plugins/hoster/UpstoreNet.py | 73 +++ module/plugins/hoster/UptoboxCom.py | 34 + module/plugins/hoster/VeehdCom.py | 81 +++ module/plugins/hoster/VeohCom.py | 53 ++ module/plugins/hoster/VidPlayNet.py | 26 + module/plugins/hoster/VimeoCom.py | 75 +++ module/plugins/hoster/Vipleech4UCom.py | 18 + module/plugins/hoster/WarserverCz.py | 18 + module/plugins/hoster/WebshareCz.py | 62 ++ module/plugins/hoster/WrzucTo.py | 52 ++ module/plugins/hoster/WuploadCom.py | 19 + module/plugins/hoster/X7To.py | 18 + module/plugins/hoster/XFileSharingPro.py | 57 ++ module/plugins/hoster/XHamsterCom.py | 129 ++++ module/plugins/hoster/XVideosCom.py | 28 + module/plugins/hoster/Xdcc.py | 207 ++++++ module/plugins/hoster/YibaishiwuCom.py | 55 ++ module/plugins/hoster/YoupornCom.py | 60 ++ module/plugins/hoster/YourfilesTo.py | 87 +++ module/plugins/hoster/YoutubeCom.py | 185 ++++++ module/plugins/hoster/ZDF.py | 59 ++ module/plugins/hoster/ZShareNet.py | 19 + module/plugins/hoster/ZeveraCom.py | 42 ++ module/plugins/hoster/ZippyshareCom.py | 65 ++ module/plugins/hoster/__init__.py | 1 + module/plugins/internal/AbstractExtractor.py | 109 ++++ module/plugins/internal/BasePlugin.py | 106 +++ module/plugins/internal/DeadCrypter.py | 32 + module/plugins/internal/DeadHoster.py | 32 + module/plugins/internal/MultiHoster.py | 202 ++++++ module/plugins/internal/SimpleCrypter.py | 152 +++++ module/plugins/internal/SimpleHoster.py | 530 +++++++++++++++ module/plugins/internal/UnRar.py | 221 +++++++ module/plugins/internal/UnZip.py | 41 ++ module/plugins/internal/UpdateManager.py | 300 +++++++++ module/plugins/internal/XFSAccount.py | 155 +++++ module/plugins/internal/XFSCrypter.py | 29 + module/plugins/internal/XFSHoster.py | 339 ++++++++++ module/plugins/internal/__init__.py | 1 + module/plugins/ocr/GigasizeCom.py | 24 + module/plugins/ocr/LinksaveIn.py | 158 +++++ module/plugins/ocr/NetloadIn.py | 29 + module/plugins/ocr/ShareonlineBiz.py | 39 ++ module/plugins/ocr/__init__.py | 1 + 442 files changed, 28211 insertions(+) create mode 100644 module/plugins/Account.py create mode 100644 module/plugins/Addon.py create mode 100644 module/plugins/Captcha.py create mode 100644 module/plugins/Container.py create mode 100644 module/plugins/Crypter.py create mode 100644 module/plugins/Hoster.py create mode 100644 module/plugins/OCR.py create mode 100644 module/plugins/Plugin.py create mode 100644 module/plugins/__init__.py create mode 100644 module/plugins/accounts/AlldebridCom.py create mode 100644 module/plugins/accounts/BayfilesCom.py create mode 100644 module/plugins/accounts/BillionuploadsCom.py create mode 100644 module/plugins/accounts/BitshareCom.py create mode 100644 module/plugins/accounts/CatShareNet.py create mode 100644 module/plugins/accounts/CramitIn.py create mode 100644 module/plugins/accounts/CzshareCom.py create mode 100644 module/plugins/accounts/DebridItaliaCom.py create mode 100644 module/plugins/accounts/DepositfilesCom.py create mode 100644 module/plugins/accounts/DropboxCom.py create mode 100644 module/plugins/accounts/EasybytezCom.py create mode 100644 module/plugins/accounts/EuroshareEu.py create mode 100644 module/plugins/accounts/FastixRu.py create mode 100644 module/plugins/accounts/FastshareCz.py create mode 100644 module/plugins/accounts/File4SafeCom.py create mode 100644 module/plugins/accounts/FileParadoxIn.py create mode 100644 module/plugins/accounts/FilecloudIo.py create mode 100644 module/plugins/accounts/FilefactoryCom.py create mode 100644 module/plugins/accounts/FilejungleCom.py create mode 100644 module/plugins/accounts/FileomCom.py create mode 100644 module/plugins/accounts/FilerNet.py create mode 100644 module/plugins/accounts/FilerioCom.py create mode 100644 module/plugins/accounts/FilesMailRu.py create mode 100644 module/plugins/accounts/FileserveCom.py create mode 100644 module/plugins/accounts/FourSharedCom.py create mode 100644 module/plugins/accounts/FreakshareCom.py create mode 100644 module/plugins/accounts/FreeWayMe.py create mode 100644 module/plugins/accounts/FshareVn.py create mode 100644 module/plugins/accounts/Ftp.py create mode 100644 module/plugins/accounts/HellshareCz.py create mode 100644 module/plugins/accounts/Http.py create mode 100644 module/plugins/accounts/HugefilesNet.py create mode 100644 module/plugins/accounts/HundredEightyUploadCom.py create mode 100644 module/plugins/accounts/JunocloudMe.py create mode 100644 module/plugins/accounts/Keep2ShareCc.py create mode 100644 module/plugins/accounts/LetitbitNet.py create mode 100644 module/plugins/accounts/LinestorageCom.py create mode 100644 module/plugins/accounts/LinksnappyCom.py create mode 100644 module/plugins/accounts/LomafileCom.py create mode 100644 module/plugins/accounts/MegaDebridEu.py create mode 100644 module/plugins/accounts/MegaRapidCz.py create mode 100644 module/plugins/accounts/MegasharesCom.py create mode 100644 module/plugins/accounts/MovReelCom.py create mode 100644 module/plugins/accounts/MultishareCz.py create mode 100644 module/plugins/accounts/MyfastfileCom.py create mode 100644 module/plugins/accounts/NetloadIn.py create mode 100644 module/plugins/accounts/NosuploadCom.py create mode 100644 module/plugins/accounts/NovafileCom.py create mode 100644 module/plugins/accounts/NowVideoSx.py create mode 100644 module/plugins/accounts/OboomCom.py create mode 100644 module/plugins/accounts/OneFichierCom.py create mode 100644 module/plugins/accounts/OverLoadMe.py create mode 100644 module/plugins/accounts/PremiumTo.py create mode 100644 module/plugins/accounts/PremiumizeMe.py create mode 100644 module/plugins/accounts/QuickshareCz.py create mode 100644 module/plugins/accounts/RPNetBiz.py create mode 100644 module/plugins/accounts/RapidfileshareNet.py create mode 100644 module/plugins/accounts/RapidgatorNet.py create mode 100644 module/plugins/accounts/RapiduNet.py create mode 100644 module/plugins/accounts/RarefileNet.py create mode 100644 module/plugins/accounts/RealdebridCom.py create mode 100644 module/plugins/accounts/RehostTo.py create mode 100644 module/plugins/accounts/RyushareCom.py create mode 100644 module/plugins/accounts/SafesharingEu.py create mode 100644 module/plugins/accounts/SecureUploadEu.py create mode 100644 module/plugins/accounts/SendmywayCom.py create mode 100644 module/plugins/accounts/ShareonlineBiz.py create mode 100644 module/plugins/accounts/SimplyPremiumCom.py create mode 100644 module/plugins/accounts/SimplydebridCom.py create mode 100644 module/plugins/accounts/StahnuTo.py create mode 100644 module/plugins/accounts/StreamcloudEu.py create mode 100644 module/plugins/accounts/TurbobitNet.py create mode 100644 module/plugins/accounts/TusfilesNet.py create mode 100644 module/plugins/accounts/UlozTo.py create mode 100644 module/plugins/accounts/UnrestrictLi.py create mode 100644 module/plugins/accounts/UploadcCom.py create mode 100644 module/plugins/accounts/UploadedTo.py create mode 100644 module/plugins/accounts/UploadheroCom.py create mode 100644 module/plugins/accounts/UploadingCom.py create mode 100644 module/plugins/accounts/UptoboxCom.py create mode 100644 module/plugins/accounts/VidPlayNet.py create mode 100644 module/plugins/accounts/XFileSharingPro.py create mode 100644 module/plugins/accounts/YibaishiwuCom.py create mode 100644 module/plugins/accounts/ZeveraCom.py create mode 100644 module/plugins/accounts/__init__.py create mode 100644 module/plugins/addon/Checksum.py create mode 100644 module/plugins/addon/ClickAndLoad.py create mode 100644 module/plugins/addon/DeleteFinished.py create mode 100644 module/plugins/addon/DownloadScheduler.py create mode 100644 module/plugins/addon/ExternalScripts.py create mode 100644 module/plugins/addon/ExtractArchive.py create mode 100644 module/plugins/addon/HotFolder.py create mode 100644 module/plugins/addon/IRCInterface.py create mode 100644 module/plugins/addon/MergeFiles.py create mode 100644 module/plugins/addon/MultiHome.py create mode 100644 module/plugins/addon/RestartFailed.py create mode 100644 module/plugins/addon/RestartSlow.py create mode 100644 module/plugins/addon/SkipRev.py create mode 100644 module/plugins/addon/UnSkipOnFail.py create mode 100644 module/plugins/addon/UpdateManager.py create mode 100644 module/plugins/addon/WindowsPhoneToastNotify.py create mode 100644 module/plugins/addon/XMPPInterface.py create mode 100644 module/plugins/addon/__init__.py create mode 100644 module/plugins/captcha/AdYouLike.py create mode 100644 module/plugins/captcha/AdsCaptcha.py create mode 100644 module/plugins/captcha/ReCaptcha.py create mode 100644 module/plugins/captcha/SolveMedia.py create mode 100644 module/plugins/captcha/__init__.py create mode 100644 module/plugins/container/CCF.py create mode 100644 module/plugins/container/LinkList.py create mode 100644 module/plugins/container/RSDF.py create mode 100644 module/plugins/container/__init__.py create mode 100644 module/plugins/crypter/BitshareCom.py create mode 100644 module/plugins/crypter/C1NeonCom.py create mode 100644 module/plugins/crypter/ChipDe.py create mode 100644 module/plugins/crypter/CrockoCom.py create mode 100644 module/plugins/crypter/CryptItCom.py create mode 100644 module/plugins/crypter/CzshareCom.py create mode 100644 module/plugins/crypter/DDLMusicOrg.py create mode 100644 module/plugins/crypter/DailymotionBatch.py create mode 100644 module/plugins/crypter/DataHu.py create mode 100644 module/plugins/crypter/DdlstorageCom.py create mode 100644 module/plugins/crypter/DepositfilesCom.py create mode 100644 module/plugins/crypter/Dereferer.py create mode 100644 module/plugins/crypter/DevhostStFolder.py create mode 100644 module/plugins/crypter/DlProtectCom.py create mode 100644 module/plugins/crypter/DontKnowMe.py create mode 100644 module/plugins/crypter/DuckCryptInfo.py create mode 100644 module/plugins/crypter/DuploadOrg.py create mode 100644 module/plugins/crypter/EasybytezCom.py create mode 100644 module/plugins/crypter/EmbeduploadCom.py create mode 100644 module/plugins/crypter/FilebeerInfo.py create mode 100644 module/plugins/crypter/FilecloudIo.py create mode 100644 module/plugins/crypter/FilecryptCc.py create mode 100644 module/plugins/crypter/FilefactoryCom.py create mode 100644 module/plugins/crypter/FilerNet.py create mode 100644 module/plugins/crypter/FileserveCom.py create mode 100644 module/plugins/crypter/FilesonicCom.py create mode 100644 module/plugins/crypter/FilestubeCom.py create mode 100644 module/plugins/crypter/FiletramCom.py create mode 100644 module/plugins/crypter/FiredriveCom.py create mode 100644 module/plugins/crypter/FourChanOrg.py create mode 100644 module/plugins/crypter/FreakhareCom.py create mode 100644 module/plugins/crypter/FreetexthostCom.py create mode 100644 module/plugins/crypter/FshareVn.py create mode 100644 module/plugins/crypter/Go4UpCom.py create mode 100644 module/plugins/crypter/GooGl.py create mode 100644 module/plugins/crypter/HoerbuchIn.py create mode 100644 module/plugins/crypter/HotfileCom.py create mode 100644 module/plugins/crypter/ILoadTo.py create mode 100644 module/plugins/crypter/ImgurComAlbum.py create mode 100644 module/plugins/crypter/JunocloudMe.py create mode 100644 module/plugins/crypter/LetitbitNet.py create mode 100644 module/plugins/crypter/LinkCryptWs.py create mode 100644 module/plugins/crypter/LinkSaveIn.py create mode 100644 module/plugins/crypter/LinkdecrypterCom.py create mode 100644 module/plugins/crypter/LixIn.py create mode 100644 module/plugins/crypter/LofCc.py create mode 100644 module/plugins/crypter/MBLinkInfo.py create mode 100644 module/plugins/crypter/MediafireCom.py create mode 100644 module/plugins/crypter/MegaRapidCz.py create mode 100644 module/plugins/crypter/MegauploadCom.py create mode 100644 module/plugins/crypter/Movie2KTo.py create mode 100644 module/plugins/crypter/MultiUpOrg.py create mode 100644 module/plugins/crypter/MultiloadCz.py create mode 100644 module/plugins/crypter/MultiuploadCom.py create mode 100644 module/plugins/crypter/NCryptIn.py create mode 100644 module/plugins/crypter/NetfolderIn.py create mode 100644 module/plugins/crypter/NosvideoCom.py create mode 100644 module/plugins/crypter/OneKhDe.py create mode 100644 module/plugins/crypter/OronCom.py create mode 100644 module/plugins/crypter/PastebinCom.py create mode 100644 module/plugins/crypter/QuickshareCz.py create mode 100644 module/plugins/crypter/RSLayerCom.py create mode 100644 module/plugins/crypter/RapidfileshareNet.py create mode 100644 module/plugins/crypter/RelinkUs.py create mode 100644 module/plugins/crypter/SafelinkingNet.py create mode 100644 module/plugins/crypter/SecuredIn.py create mode 100644 module/plugins/crypter/SexuriaCom.py create mode 100644 module/plugins/crypter/ShareLinksBiz.py create mode 100644 module/plugins/crypter/SharingmatrixCom.py create mode 100644 module/plugins/crypter/SpeedLoadOrg.py create mode 100644 module/plugins/crypter/StealthTo.py create mode 100644 module/plugins/crypter/TnyCz.py create mode 100644 module/plugins/crypter/TrailerzoneInfo.py create mode 100644 module/plugins/crypter/TurbobitNet.py create mode 100644 module/plugins/crypter/TusfilesNet.py create mode 100644 module/plugins/crypter/UlozTo.py create mode 100644 module/plugins/crypter/UploadableCh.py create mode 100644 module/plugins/crypter/UploadedTo.py create mode 100644 module/plugins/crypter/WiiReloadedOrg.py create mode 100644 module/plugins/crypter/WuploadCom.py create mode 100644 module/plugins/crypter/XFileSharingPro.py create mode 100644 module/plugins/crypter/XupPl.py create mode 100644 module/plugins/crypter/YoutubeBatch.py create mode 100644 module/plugins/crypter/__init__.py create mode 100644 module/plugins/hooks/AlldebridCom.py create mode 100644 module/plugins/hooks/BypassCaptcha.py create mode 100644 module/plugins/hooks/Captcha9Kw.py create mode 100644 module/plugins/hooks/CaptchaBrotherhood.py create mode 100644 module/plugins/hooks/DeathByCaptcha.py create mode 100644 module/plugins/hooks/DebridItaliaCom.py create mode 100644 module/plugins/hooks/EasybytezCom.py create mode 100644 module/plugins/hooks/ExpertDecoders.py create mode 100644 module/plugins/hooks/FastixRu.py create mode 100644 module/plugins/hooks/FreeWayMe.py create mode 100644 module/plugins/hooks/ImageTyperz.py create mode 100644 module/plugins/hooks/LinkdecrypterCom.py create mode 100644 module/plugins/hooks/LinksnappyCom.py create mode 100644 module/plugins/hooks/MegaDebridEu.py create mode 100644 module/plugins/hooks/MultishareCz.py create mode 100644 module/plugins/hooks/MyfastfileCom.py create mode 100644 module/plugins/hooks/OverLoadMe.py create mode 100644 module/plugins/hooks/PremiumTo.py create mode 100644 module/plugins/hooks/PremiumizeMe.py create mode 100644 module/plugins/hooks/RPNetBiz.py create mode 100644 module/plugins/hooks/RealdebridCom.py create mode 100644 module/plugins/hooks/RehostTo.py create mode 100644 module/plugins/hooks/SimplyPremiumCom.py create mode 100644 module/plugins/hooks/SimplydebridCom.py create mode 100644 module/plugins/hooks/UnrestrictLi.py create mode 100644 module/plugins/hooks/XFileSharingPro.py create mode 100644 module/plugins/hooks/ZeveraCom.py create mode 100644 module/plugins/hooks/__init__.py create mode 100644 module/plugins/hoster/AlldebridCom.py create mode 100644 module/plugins/hoster/BayfilesCom.py create mode 100644 module/plugins/hoster/BezvadataCz.py create mode 100644 module/plugins/hoster/BillionuploadsCom.py create mode 100644 module/plugins/hoster/BitshareCom.py create mode 100644 module/plugins/hoster/BoltsharingCom.py create mode 100644 module/plugins/hoster/CatShareNet.py create mode 100644 module/plugins/hoster/CloudzerNet.py create mode 100644 module/plugins/hoster/CramitIn.py create mode 100644 module/plugins/hoster/CrockoCom.py create mode 100644 module/plugins/hoster/CyberlockerCh.py create mode 100644 module/plugins/hoster/CzshareCom.py create mode 100644 module/plugins/hoster/DailymotionCom.py create mode 100644 module/plugins/hoster/DataHu.py create mode 100644 module/plugins/hoster/DataportCz.py create mode 100644 module/plugins/hoster/DateiTo.py create mode 100644 module/plugins/hoster/DdlstorageCom.py create mode 100644 module/plugins/hoster/DebridItaliaCom.py create mode 100644 module/plugins/hoster/DepositfilesCom.py create mode 100644 module/plugins/hoster/DevhostSt.py create mode 100644 module/plugins/hoster/DlFreeFr.py create mode 100644 module/plugins/hoster/DodanePl.py create mode 100644 module/plugins/hoster/DuploadOrg.py create mode 100644 module/plugins/hoster/EasybytezCom.py create mode 100644 module/plugins/hoster/EdiskCz.py create mode 100644 module/plugins/hoster/EgoFilesCom.py create mode 100644 module/plugins/hoster/EnteruploadCom.py create mode 100644 module/plugins/hoster/EpicShareNet.py create mode 100644 module/plugins/hoster/EuroshareEu.py create mode 100644 module/plugins/hoster/ExtabitCom.py create mode 100644 module/plugins/hoster/FastixRu.py create mode 100644 module/plugins/hoster/FastshareCz.py create mode 100644 module/plugins/hoster/FileApeCom.py create mode 100644 module/plugins/hoster/FileParadoxIn.py create mode 100644 module/plugins/hoster/FileSharkPl.py create mode 100644 module/plugins/hoster/FileStoreTo.py create mode 100644 module/plugins/hoster/FilebeerInfo.py create mode 100644 module/plugins/hoster/FilecloudIo.py create mode 100644 module/plugins/hoster/FilefactoryCom.py create mode 100644 module/plugins/hoster/FilejungleCom.py create mode 100644 module/plugins/hoster/FileomCom.py create mode 100644 module/plugins/hoster/FilepostCom.py create mode 100644 module/plugins/hoster/FilepupNet.py create mode 100644 module/plugins/hoster/FilerNet.py create mode 100644 module/plugins/hoster/FilerioCom.py create mode 100644 module/plugins/hoster/FilesMailRu.py create mode 100644 module/plugins/hoster/FileserveCom.py create mode 100644 module/plugins/hoster/FileshareInUa.py create mode 100644 module/plugins/hoster/FilesonicCom.py create mode 100644 module/plugins/hoster/FilezyNet.py create mode 100644 module/plugins/hoster/FiredriveCom.py create mode 100644 module/plugins/hoster/FlyFilesNet.py create mode 100644 module/plugins/hoster/FourSharedCom.py create mode 100644 module/plugins/hoster/FreakshareCom.py create mode 100644 module/plugins/hoster/FreeWayMe.py create mode 100644 module/plugins/hoster/FreevideoCz.py create mode 100644 module/plugins/hoster/FshareVn.py create mode 100644 module/plugins/hoster/Ftp.py create mode 100644 module/plugins/hoster/GamefrontCom.py create mode 100644 module/plugins/hoster/GigapetaCom.py create mode 100644 module/plugins/hoster/GooIm.py create mode 100644 module/plugins/hoster/HellshareCz.py create mode 100644 module/plugins/hoster/HellspyCz.py create mode 100644 module/plugins/hoster/HotfileCom.py create mode 100644 module/plugins/hoster/HugefilesNet.py create mode 100644 module/plugins/hoster/HundredEightyUploadCom.py create mode 100644 module/plugins/hoster/IFileWs.py create mode 100644 module/plugins/hoster/IcyFilesCom.py create mode 100644 module/plugins/hoster/IfileIt.py create mode 100644 module/plugins/hoster/IfolderRu.py create mode 100644 module/plugins/hoster/JumbofilesCom.py create mode 100644 module/plugins/hoster/JunocloudMe.py create mode 100644 module/plugins/hoster/Keep2ShareCc.py create mode 100644 module/plugins/hoster/KickloadCom.py create mode 100644 module/plugins/hoster/KingfilesNet.py create mode 100644 module/plugins/hoster/LemUploadsCom.py create mode 100644 module/plugins/hoster/LetitbitNet.py create mode 100644 module/plugins/hoster/LinksnappyCom.py create mode 100644 module/plugins/hoster/LoadTo.py create mode 100644 module/plugins/hoster/LomafileCom.py create mode 100644 module/plugins/hoster/LuckyShareNet.py create mode 100644 module/plugins/hoster/MediafireCom.py create mode 100644 module/plugins/hoster/MegaCoNz.py create mode 100644 module/plugins/hoster/MegaDebridEu.py create mode 100644 module/plugins/hoster/MegaFilesSe.py create mode 100644 module/plugins/hoster/MegaRapidCz.py create mode 100644 module/plugins/hoster/MegacrypterCom.py create mode 100644 module/plugins/hoster/MegareleaseOrg.py create mode 100644 module/plugins/hoster/MegasharesCom.py create mode 100644 module/plugins/hoster/MegauploadCom.py create mode 100644 module/plugins/hoster/MegavideoCom.py create mode 100644 module/plugins/hoster/MovReelCom.py create mode 100644 module/plugins/hoster/MultishareCz.py create mode 100644 module/plugins/hoster/MyfastfileCom.py create mode 100644 module/plugins/hoster/MyvideoDe.py create mode 100644 module/plugins/hoster/NahrajCz.py create mode 100644 module/plugins/hoster/NarodRu.py create mode 100644 module/plugins/hoster/NetloadIn.py create mode 100644 module/plugins/hoster/NosuploadCom.py create mode 100644 module/plugins/hoster/NovafileCom.py create mode 100644 module/plugins/hoster/NowDownloadSx.py create mode 100644 module/plugins/hoster/NowVideoSx.py create mode 100644 module/plugins/hoster/OboomCom.py create mode 100644 module/plugins/hoster/OneFichierCom.py create mode 100644 module/plugins/hoster/OronCom.py create mode 100644 module/plugins/hoster/OverLoadMe.py create mode 100644 module/plugins/hoster/PandaplaNet.py create mode 100644 module/plugins/hoster/PornhostCom.py create mode 100644 module/plugins/hoster/PornhubCom.py create mode 100644 module/plugins/hoster/PotloadCom.py create mode 100644 module/plugins/hoster/PremiumTo.py create mode 100644 module/plugins/hoster/PremiumizeMe.py create mode 100644 module/plugins/hoster/PromptfileCom.py create mode 100644 module/plugins/hoster/PrzeklejPl.py create mode 100644 module/plugins/hoster/QuickshareCz.py create mode 100644 module/plugins/hoster/RPNetBiz.py create mode 100644 module/plugins/hoster/RapidfileshareNet.py create mode 100644 module/plugins/hoster/RapidgatorNet.py create mode 100644 module/plugins/hoster/RapiduNet.py create mode 100644 module/plugins/hoster/RarefileNet.py create mode 100644 module/plugins/hoster/RealdebridCom.py create mode 100644 module/plugins/hoster/RedtubeCom.py create mode 100644 module/plugins/hoster/RehostTo.py create mode 100644 module/plugins/hoster/RemixshareCom.py create mode 100644 module/plugins/hoster/RgHostNet.py create mode 100644 module/plugins/hoster/RyushareCom.py create mode 100644 module/plugins/hoster/SafesharingEu.py create mode 100644 module/plugins/hoster/SecureUploadEu.py create mode 100644 module/plugins/hoster/SendmywayCom.py create mode 100644 module/plugins/hoster/SendspaceCom.py create mode 100644 module/plugins/hoster/Share4WebCom.py create mode 100644 module/plugins/hoster/Share76Com.py create mode 100644 module/plugins/hoster/ShareFilesCo.py create mode 100644 module/plugins/hoster/SharebeesCom.py create mode 100644 module/plugins/hoster/ShareonlineBiz.py create mode 100644 module/plugins/hoster/ShareplaceCom.py create mode 100644 module/plugins/hoster/SharingmatrixCom.py create mode 100644 module/plugins/hoster/ShragleCom.py create mode 100644 module/plugins/hoster/SimplyPremiumCom.py create mode 100644 module/plugins/hoster/SimplydebridCom.py create mode 100644 module/plugins/hoster/SockshareCom.py create mode 100644 module/plugins/hoster/SoundcloudCom.py create mode 100644 module/plugins/hoster/SpeedLoadOrg.py create mode 100644 module/plugins/hoster/SpeedfileCz.py create mode 100644 module/plugins/hoster/SpeedyshareCom.py create mode 100644 module/plugins/hoster/StorageTo.py create mode 100644 module/plugins/hoster/StreamCz.py create mode 100644 module/plugins/hoster/StreamcloudEu.py create mode 100644 module/plugins/hoster/TurbobitNet.py create mode 100644 module/plugins/hoster/TurbouploadCom.py create mode 100644 module/plugins/hoster/TusfilesNet.py create mode 100644 module/plugins/hoster/TwoSharedCom.py create mode 100644 module/plugins/hoster/UlozTo.py create mode 100644 module/plugins/hoster/UloziskoSk.py create mode 100644 module/plugins/hoster/UnibytesCom.py create mode 100644 module/plugins/hoster/UnrestrictLi.py create mode 100644 module/plugins/hoster/UpleaCom.py create mode 100644 module/plugins/hoster/UploadStationCom.py create mode 100644 module/plugins/hoster/UploadableCh.py create mode 100644 module/plugins/hoster/UploadboxCom.py create mode 100644 module/plugins/hoster/UploadedTo.py create mode 100644 module/plugins/hoster/UploadhereCom.py create mode 100644 module/plugins/hoster/UploadheroCom.py create mode 100644 module/plugins/hoster/UploadingCom.py create mode 100644 module/plugins/hoster/UploadkingCom.py create mode 100644 module/plugins/hoster/UpstoreNet.py create mode 100644 module/plugins/hoster/UptoboxCom.py create mode 100644 module/plugins/hoster/VeehdCom.py create mode 100644 module/plugins/hoster/VeohCom.py create mode 100644 module/plugins/hoster/VidPlayNet.py create mode 100644 module/plugins/hoster/VimeoCom.py create mode 100644 module/plugins/hoster/Vipleech4UCom.py create mode 100644 module/plugins/hoster/WarserverCz.py create mode 100644 module/plugins/hoster/WebshareCz.py create mode 100644 module/plugins/hoster/WrzucTo.py create mode 100644 module/plugins/hoster/WuploadCom.py create mode 100644 module/plugins/hoster/X7To.py create mode 100644 module/plugins/hoster/XFileSharingPro.py create mode 100644 module/plugins/hoster/XHamsterCom.py create mode 100644 module/plugins/hoster/XVideosCom.py create mode 100644 module/plugins/hoster/Xdcc.py create mode 100644 module/plugins/hoster/YibaishiwuCom.py create mode 100644 module/plugins/hoster/YoupornCom.py create mode 100644 module/plugins/hoster/YourfilesTo.py create mode 100644 module/plugins/hoster/YoutubeCom.py create mode 100644 module/plugins/hoster/ZDF.py create mode 100644 module/plugins/hoster/ZShareNet.py create mode 100644 module/plugins/hoster/ZeveraCom.py create mode 100644 module/plugins/hoster/ZippyshareCom.py create mode 100644 module/plugins/hoster/__init__.py create mode 100644 module/plugins/internal/AbstractExtractor.py create mode 100644 module/plugins/internal/BasePlugin.py create mode 100644 module/plugins/internal/DeadCrypter.py create mode 100644 module/plugins/internal/DeadHoster.py create mode 100644 module/plugins/internal/MultiHoster.py create mode 100644 module/plugins/internal/SimpleCrypter.py create mode 100644 module/plugins/internal/SimpleHoster.py create mode 100644 module/plugins/internal/UnRar.py create mode 100644 module/plugins/internal/UnZip.py create mode 100644 module/plugins/internal/UpdateManager.py create mode 100644 module/plugins/internal/XFSAccount.py create mode 100644 module/plugins/internal/XFSCrypter.py create mode 100644 module/plugins/internal/XFSHoster.py create mode 100644 module/plugins/internal/__init__.py create mode 100644 module/plugins/ocr/GigasizeCom.py create mode 100644 module/plugins/ocr/LinksaveIn.py create mode 100644 module/plugins/ocr/NetloadIn.py create mode 100644 module/plugins/ocr/ShareonlineBiz.py create mode 100644 module/plugins/ocr/__init__.py (limited to 'module/plugins') diff --git a/module/plugins/Account.py b/module/plugins/Account.py new file mode 100644 index 000000000..f8014908f --- /dev/null +++ b/module/plugins/Account.py @@ -0,0 +1,307 @@ +# -*- coding: utf-8 -*- + +from random import choice +from time import time +from traceback import print_exc +from threading import RLock + +from pyload.plugin.Plugin import Base +from pyload.utils import compare_time, parseFileSize, lock + + +class WrongPassword(Exception): + pass + + +class Account(Base): + """ + Base class for every Account plugin. + Just overwrite `login` and cookies will be stored and account becomes accessible in\ + associated hoster plugin. Plugin should also provide `loadAccountInfo` + """ + __name__ = "Account" + __type__ = "account" + __version__ = "0.03" + + __description__ = """Base account plugin""" + __license__ = "GPLv3" + __authors__ = [("mkaay", "mkaay@mkaay.de")] + + + #: after that time (in minutes) pyload will relogin the account + login_timeout = 10 * 60 + #: after that time (in minutes) account data will be reloaded + info_threshold = 10 * 60 + + + def __init__(self, manager, accounts): + Base.__init__(self, manager.core) + + self.manager = manager + self.accounts = {} + self.infos = {} #: cache for account information + self.lock = RLock() + self.timestamps = {} + + self.init() + + self.setAccounts(accounts) + + + def init(self): + pass + + + def login(self, user, data, req): + """login into account, the cookies will be saved so user can be recognized + + :param user: loginname + :param data: data dictionary + :param req: `Request` instance + """ + pass + + + @lock + def _login(self, user, data): + # set timestamp for login + self.timestamps[user] = time() + + req = self.getAccountRequest(user) + try: + self.login(user, data, req) + except WrongPassword: + self.logWarning( + _("Could not login with account %(user)s | %(msg)s") % {"user": user, + "msg": _("Wrong Password")}) + success = data['valid'] = False + except Exception, e: + self.logWarning( + _("Could not login with account %(user)s | %(msg)s") % {"user": user, + "msg": e}) + success = data['valid'] = False + if self.core.debug: + print_exc() + else: + success = True + finally: + if req: + req.close() + return success + + + def relogin(self, user): + req = self.getAccountRequest(user) + if req: + req.cj.clear() + req.close() + if user in self.infos: + del self.infos[user] #delete old information + + return self._login(user, self.accounts[user]) + + + def setAccounts(self, accounts): + self.accounts = accounts + for user, data in self.accounts.iteritems(): + self._login(user, data) + self.infos[user] = {} + + + def updateAccounts(self, user, password=None, options={}): + """ updates account and return true if anything changed """ + + if user in self.accounts: + self.accounts[user]['valid'] = True #do not remove or accounts will not login + if password: + self.accounts[user]['password'] = password + self.relogin(user) + return True + if options: + before = self.accounts[user]['options'] + self.accounts[user]['options'].update(options) + return self.accounts[user]['options'] != before + else: + self.accounts[user] = {"password": password, "options": options, "valid": True} + self._login(user, self.accounts[user]) + return True + + + def removeAccount(self, user): + if user in self.accounts: + del self.accounts[user] + if user in self.infos: + del self.infos[user] + if user in self.timestamps: + del self.timestamps[user] + + + @lock + def getAccountInfo(self, name, force=False): + """retrieve account infos for an user, do **not** overwrite this method!\\ + just use it to retrieve infos in hoster plugins. see `loadAccountInfo` + + :param name: username + :param force: reloads cached account information + :return: dictionary with information + """ + data = Account.loadAccountInfo(self, name) + + if force or name not in self.infos: + self.logDebug("Get Account Info for %s" % name) + req = self.getAccountRequest(name) + + try: + infos = self.loadAccountInfo(name, req) + if not type(infos) == dict: + raise Exception("Wrong return format") + except Exception, e: + infos = {"error": str(e)} + print_exc() + + if req: + req.close() + + self.logDebug("Account Info: %s" % infos) + + infos['timestamp'] = time() + self.infos[name] = infos + elif "timestamp" in self.infos[name] and self.infos[name][ + "timestamp"] + self.info_threshold * 60 < time(): + self.logDebug("Reached timeout for account data") + self.scheduleRefresh(name) + + data.update(self.infos[name]) + return data + + + def isPremium(self, user): + info = self.getAccountInfo(user) + return info['premium'] + + + def loadAccountInfo(self, name, req=None): + """this should be overwritten in account plugin,\ + and retrieving account information for user + + :param name: + :param req: `Request` instance + :return: + """ + return {"validuntil" : None, #: -1 for unlimited + "login" : name, + # "password" : self.accounts[name]['password'], #: commented due security reason + "options" : self.accounts[name]['options'], + "valid" : self.accounts[name]['valid'], + "trafficleft": None, #: in bytes, -1 for unlimited + "maxtraffic" : None, + "premium" : None, + "timestamp" : 0, #: time this info was retrieved + "type" : self.__name__} + + + def getAllAccounts(self, force=False): + return [self.getAccountInfo(user, force) for user, data in self.accounts.iteritems()] + + + def getAccountRequest(self, user=None): + if not user: + user, data = self.selectAccount() + if not user: + return None + + req = self.core.requestFactory.getRequest(self.__name__, user) + return req + + + def getAccountCookies(self, user=None): + if not user: + user, data = self.selectAccount() + if not user: + return None + + cj = self.core.requestFactory.getCookieJar(self.__name__, user) + return cj + + + def getAccountData(self, user): + return self.accounts[user] + + + def selectAccount(self): + """ returns an valid account name and data""" + usable = [] + for user, data in self.accounts.iteritems(): + if not data['valid']: continue + + if "time" in data['options'] and data['options']['time']: + time_data = "" + try: + time_data = data['options']['time'][0] + start, end = time_data.split("-") + if not compare_time(start.split(":"), end.split(":")): + continue + except Exception: + self.logWarning(_("Your Time %s has wrong format, use: 1:22-3:44") % time_data) + + if user in self.infos: + if "validuntil" in self.infos[user]: + if self.infos[user]['validuntil'] > 0 and time() > self.infos[user]['validuntil']: + continue + if "trafficleft" in self.infos[user]: + if self.infos[user]['trafficleft'] == 0: + continue + + usable.append((user, data)) + + if not usable: return None, None + return choice(usable) + + + def canUse(self): + return False if self.selectAccount() == (None, None) else True + + + def parseTraffic(self, value, unit=None): #: return bytes + if not unit and not isinstance(value, basestring): + unit = "KB" + return parseFileSize(value, unit) + + + def wrongPassword(self): + raise WrongPassword + + + def empty(self, user): + if user in self.infos: + self.logWarning(_("Account %s has not enough traffic, checking again in 30min") % user) + + self.infos[user].update({"trafficleft": 0}) + self.scheduleRefresh(user, 30 * 60) + + + def expired(self, user): + if user in self.infos: + self.logWarning(_("Account %s is expired, checking again in 1h") % user) + + self.infos[user].update({"validuntil": time() - 1}) + self.scheduleRefresh(user, 60 * 60) + + + def scheduleRefresh(self, user, time=0, force=True): + """ add task to refresh account info to sheduler """ + self.logDebug("Scheduled Account refresh for %s in %s seconds." % (user, time)) + self.core.scheduler.addJob(time, self.getAccountInfo, [user, force]) + + + @lock + def checkLogin(self, user): + """ checks if user is still logged in """ + if user in self.timestamps: + if self.login_timeout > 0 and self.timestamps[user] + self.login_timeout * 60 < time(): + self.logDebug("Reached login timeout for %s" % user) + return self.relogin(user) + else: + return True + else: + return False diff --git a/module/plugins/Addon.py b/module/plugins/Addon.py new file mode 100644 index 000000000..ca36fe4ae --- /dev/null +++ b/module/plugins/Addon.py @@ -0,0 +1,185 @@ +# -*- coding: utf-8 -*- + +from traceback import print_exc + +from pyload.plugin.Plugin import Base +from pyload.utils import has_method + + +class Expose(object): + """ used for decoration to declare rpc services """ + + def __new__(cls, f, *args, **kwargs): + addonManager.addRPC(f.__module__, f.func_name, f.func_doc) + return f + + +def threaded(fn): + + def run(*args,**kwargs): + addonManager.startThread(fn, *args, **kwargs) + + return run + + +class Addon(Base): + """ + Base class for addon plugins. + """ + __name__ = "Addon" + __type__ = "addon" + __version__ = "0.03" + + __config__ = [] #: [("name", "type", "desc", "default")] + + __description__ = """Base addon/hook plugin""" + __license__ = "GPLv3" + __authors__ = [("mkaay", "mkaay@mkaay.de"), + ("RaNaN", "RaNaN@pyload.org")] + + + #: automatically register event listeners for functions, attribute will be deleted dont use it yourself + event_map = {} + + # Deprecated alternative to event_map + #: List of events the plugin can handle, name the functions exactly like eventname. + event_list = [] #@NOTE: dont make duplicate entries in event_map + + + def __init__(self, core, manager): + Base.__init__(self, core) + + #: Provide information in dict here, usable by API `getInfo` + self.info = {} + + #: Callback of periodical job task, used by AddonManager + self.cb = None + self.interval = 60 + + #: `AddonManager` + self.manager = manager + + #register events + if self.event_map: + for event, funcs in self.event_map.iteritems(): + if type(funcs) in (list, tuple): + for f in funcs: + self.manager.addEvent(event, getattr(self,f)) + else: + self.manager.addEvent(event, getattr(self,funcs)) + + #delete for various reasons + self.event_map = None + + if self.event_list: + for f in self.event_list: + self.manager.addEvent(f, getattr(self,f)) + + self.event_list = None + + self.setup() + + # self.initPeriodical() + + + def initPeriodical(self, delay=0, threaded=False): + self.cb = self.core.scheduler.addJob(delay, self._periodical, args=[threaded], threaded=threaded) + + + def _periodical(self, threaded): + if self.interval < 0: + self.cb = None + return + + try: + self.periodical() + + except Exception, e: + self.logError(_("Error executing addon: %s") % e) + if self.core.debug: + print_exc() + + self.cb = self.core.scheduler.addJob(self.interval, self._periodical, threaded=threaded) + + + def __repr__(self): + return "" % self.__name__ + + + def setup(self): + """ more init stuff if needed """ + pass + + + def deactivate(self): + """ called when addon was deactivated """ + if has_method(self.__class__, "unload"): + self.unload() + + def unload(self): # Deprecated, use method deactivate() instead + pass + + + def isActivated(self): + """ checks if addon is activated""" + return self.core.config.getPlugin(self.__name__, "activated") + + + # Event methods - overwrite these if needed + def activate(self): + """ called when addon was activated """ + if has_method(self.__class__, "coreReady"): + self.coreReady() + + def coreReady(self): # Deprecated, use method activate() instead + pass + + + def exit(self): + """ called by core.shutdown just before pyLoad exit """ + if has_method(self.__class__, "coreExiting"): + self.coreExiting() + + def coreExiting(self): # Deprecated, use method exit() instead + pass + + + def downloadPreparing(self, pyfile): + pass + + + def downloadFinished(self, pyfile): + pass + + + def downloadFailed(self, pyfile): + pass + + + def packageFinished(self, pypack): + pass + + + def beforeReconnecting(self, ip): + pass + + + def afterReconnecting(self, ip): + pass + + + def periodical(self): + pass + + + def captchaTask(self, task): + """ new captcha task for the plugin, it MUST set the handler and timeout or will be ignored """ + pass + + + def captchaCorrect(self, task): + pass + + + def captchaInvalid(self, task): + pass diff --git a/module/plugins/Captcha.py b/module/plugins/Captcha.py new file mode 100644 index 000000000..ace488994 --- /dev/null +++ b/module/plugins/Captcha.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Plugin import Plugin + + +class Captcha(Plugin): + __name__ = "Captcha" + __type__ = "captcha" + __version__ = "0.14" + + __description__ = """Base captcha service plugin""" + __license__ = "GPLv3" + __authors__ = [("pyLoad Team", "admin@pyload.org")] + + + KEY_PATTERN = None + + key = None #: last key detected + + + def __init__(self, plugin): + self.plugin = plugin + + + def detect_key(self, html=None): + if not html: + if hasattr(self.plugin, "html") and self.plugin.html: + html = self.plugin.html + else: + errmsg = _("%s html not found") % self.__name__ + self.plugin.error(errmsg) + raise TypeError(errmsg) + + m = re.search(self.KEY_PATTERN, html) + if m: + self.key = m.group("KEY") + self.plugin.logDebug("%s key: %s" % (self.__name__, self.key)) + return self.key + else: + self.plugin.logDebug("%s key not found" % self.__name__) + return None + + + def challenge(self, key=None): + raise NotImplementedError + + + def result(self, server, challenge): + raise NotImplementedError diff --git a/module/plugins/Container.py b/module/plugins/Container.py new file mode 100644 index 000000000..bfc5713a7 --- /dev/null +++ b/module/plugins/Container.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import re + +from os import remove +from os.path import basename, exists + +from pyload.plugin.internal.Crypter import Crypter +from pyload.utils import safe_join + + +class Container(Crypter): + __name__ = "Container" + __type__ = "container" + __version__ = "0.01" + + __pattern__ = r'^unmatchable$' + __config__ = [] #: [("name", "type", "desc", "default")] + + __description__ = """Base container decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("mkaay", "mkaay@mkaay.de")] + + + def preprocessing(self, thread): + """prepare""" + + self.setup() + self.thread = thread + + self.loadToDisk() + + self.decrypt(self.pyfile) + self.deleteTmp() + + self.createPackages() + + + def loadToDisk(self): + """loads container to disk if its stored remotely and overwrite url, + or check existent on several places at disk""" + + if self.pyfile.url.startswith("http"): + self.pyfile.name = re.findall("([^\/=]+)", self.pyfile.url)[-1] + content = self.load(self.pyfile.url) + self.pyfile.url = safe_join(self.core.config['general']['download_folder'], self.pyfile.name) + try: + with open(self.pyfile.url, "wb") as f: + f.write(content) + except IOError, e: + self.fail(str(e)) + + else: + self.pyfile.name = basename(self.pyfile.url) + if not exists(self.pyfile.url): + if exists(safe_join(pypath, self.pyfile.url)): + self.pyfile.url = safe_join(pypath, self.pyfile.url) + else: + self.fail(_("File not exists")) + + + def deleteTmp(self): + if self.pyfile.name.startswith("tmp_"): + remove(self.pyfile.url) diff --git a/module/plugins/Crypter.py b/module/plugins/Crypter.py new file mode 100644 index 000000000..f93ee254a --- /dev/null +++ b/module/plugins/Crypter.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- + +from urlparse import urlparse + +from pyload.plugin.Plugin import Plugin +from pyload.utils import decode, safe_filename + + +class Crypter(Plugin): + __name__ = "Crypter" + __type__ = "crypter" + __version__ = "0.05" + + __pattern__ = r'^unmatchable$' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), #: Overrides core.config['general']['folder_per_package'] + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """Base decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + html = None #: last html loaded + + + def __init__(self, pyfile): + #: Put all packages here. It's a list of tuples like: ( name, [list of links], folder ) + self.packages = [] + + #: List of urls, pyLoad will generate packagenames + self.urls = [] + + Plugin.__init__(self, pyfile) + + + def process(self, pyfile): + """ main method """ + + self.decrypt(pyfile) + + if self.urls: + self.generatePackages() + + elif not self.packages: + self.error(_("No link extracted"), "decrypt") + + self.createPackages() + + + def decrypt(self, pyfile): + raise NotImplementedError + + + def generatePackages(self): + """ generate new packages from self.urls """ + + packages = map(lambda name, links: (name, links, None), self.core.api.generatePackages(self.urls).iteritems()) + self.packages.extend(packages) + + + def createPackages(self): + """ create new packages from self.packages """ + + package_folder = self.pyfile.package().folder + package_password = self.pyfile.package().password + package_queue = self.pyfile.package().queue + + folder_per_package = self.core.config['general']['folder_per_package'] + try: + use_subfolder = self.getConfig('use_subfolder') + except Exception: + use_subfolder = folder_per_package + try: + subfolder_per_package = self.getConfig('subfolder_per_package') + except Exception: + subfolder_per_package = True + + for pack in self.packages: + name, links, folder = pack + + self.logDebug("Parsed package: %s" % name, + "%d links" % len(links), + "Saved to folder: %s" % folder if folder else "Saved to download folder") + + links = map(decode, links) + + pid = self.core.api.addPackage(name, links, package_queue) + + if package_password: + self.core.api.setPackageData(pid, {"password": package_password}) + + setFolder = lambda x: self.core.api.setPackageData(pid, {"folder": x or ""}) #: Workaround to do not break API addPackage method + + if use_subfolder: + if not subfolder_per_package: + setFolder(package_folder) + self.logDebug("Set package %(name)s folder to: %(folder)s" % {"name": name, "folder": folder}) + + elif not folder_per_package or name != folder: + if not folder: + folder = urlparse(name).path.split("/")[-1] + + setFolder(safe_filename(folder)) + self.logDebug("Set package %(name)s folder to: %(folder)s" % {"name": name, "folder": folder}) + + elif folder_per_package: + setFolder(None) diff --git a/module/plugins/Hoster.py b/module/plugins/Hoster.py new file mode 100644 index 000000000..2d43ee845 --- /dev/null +++ b/module/plugins/Hoster.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Plugin import Plugin + + +def getInfo(self): + #result = [ .. (name, size, status, url) .. ] + return + + +class Hoster(Plugin): + __name__ = "Hoster" + __type__ = "hoster" + __version__ = "0.02" + + __pattern__ = r'^unmatchable$' + __config__ = [] #: [("name", "type", "desc", "default")] + + __description__ = """Base hoster plugin""" + __license__ = "GPLv3" + __authors__ = [("mkaay", "mkaay@mkaay.de")] diff --git a/module/plugins/OCR.py b/module/plugins/OCR.py new file mode 100644 index 000000000..8e60737b6 --- /dev/null +++ b/module/plugins/OCR.py @@ -0,0 +1,315 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +try: + from PIL import Image, GifImagePlugin, JpegImagePlugin, PngImagePlugin, TiffImagePlugin +except ImportError: + import Image, GifImagePlugin, JpegImagePlugin, PngImagePlugin, TiffImagePlugin + +import logging +import subprocess + +from os.path import abspath, join + + +class OCR(object): + __name__ = "OCR" + __type__ = "ocr" + __version__ = "0.10" + + __description__ = """Base OCR plugin""" + __license__ = "GPLv3" + __authors__ = [("pyLoad Team", "admin@pyload.org")] + + + def __init__(self): + self.logger = logging.getLogger("log") + + + def load_image(self, image): + self.image = Image.open(image) + self.pixels = self.image.load() + self.result_captcha = '' + + + def unload(self): + """delete all tmp images""" + pass + + + def threshold(self, value): + self.image = self.image.point(lambda a: a * value + 10) + + + def run(self, command): + """Run a command""" + + popen = subprocess.Popen(command, bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + popen.wait() + output = popen.stdout.read() + " | " + popen.stderr.read() + popen.stdout.close() + popen.stderr.close() + self.logger.debug("Tesseract ReturnCode %s Output: %s" % (popen.returncode, output)) + + + def run_tesser(self, subset=False, digits=True, lowercase=True, uppercase=True): + #tmpTif = tempfile.NamedTemporaryFile(suffix=".tif") + try: + tmpTif = open(join("tmp", "tmpTif_%s.tif" % self.__name__), "wb") + tmpTif.close() + + #tmpTxt = tempfile.NamedTemporaryFile(suffix=".txt") + tmpTxt = open(join("tmp", "tmpTxt_%s.txt" % self.__name__), "wb") + tmpTxt.close() + + except IOError, e: + self.logError(e) + return + + self.logger.debug("save tiff") + self.image.save(tmpTif.name, 'TIFF') + + if os.name == "nt": + tessparams = [join(pypath, "tesseract", "tesseract.exe")] + else: + tessparams = ['tesseract'] + + tessparams.extend([abspath(tmpTif.name), abspath(tmpTxt.name).replace(".txt", "")] ) + + if subset and (digits or lowercase or uppercase): + #tmpSub = tempfile.NamedTemporaryFile(suffix=".subset") + with open(join("tmp", "tmpSub_%s.subset" % self.__name__), "wb") as tmpSub: + tmpSub.write("tessedit_char_whitelist ") + + if digits: + tmpSub.write("0123456789") + if lowercase: + tmpSub.write("abcdefghijklmnopqrstuvwxyz") + if uppercase: + tmpSub.write("ABCDEFGHIJKLMNOPQRSTUVWXYZ") + + tmpSub.write("\n") + tessparams.append("nobatch") + tessparams.append(abspath(tmpSub.name)) + + self.logger.debug("run tesseract") + self.run(tessparams) + self.logger.debug("read txt") + + try: + with open(tmpTxt.name, 'r') as f: + self.result_captcha = f.read().replace("\n", "") + except Exception: + self.result_captcha = "" + + self.logger.debug(self.result_captcha) + try: + os.remove(tmpTif.name) + os.remove(tmpTxt.name) + if subset and (digits or lowercase or uppercase): + os.remove(tmpSub.name) + except Exception: + pass + + + def get_captcha(self, name): + raise NotImplementedError + + + def to_greyscale(self): + if self.image.mode != 'L': + self.image = self.image.convert('L') + + self.pixels = self.image.load() + + + def eval_black_white(self, limit): + self.pixels = self.image.load() + w, h = self.image.size + for x in xrange(w): + for y in xrange(h): + if self.pixels[x, y] > limit: + self.pixels[x, y] = 255 + else: + self.pixels[x, y] = 0 + + + def clean(self, allowed): + pixels = self.pixels + + w, h = self.image.size + + for x in xrange(w): + for y in xrange(h): + if pixels[x, y] == 255: + continue + # No point in processing white pixels since we only want to remove black pixel + count = 0 + + try: + if pixels[x - 1, y - 1] != 255: + count += 1 + if pixels[x - 1, y] != 255: + count += 1 + if pixels[x - 1, y + 1] != 255: + count += 1 + if pixels[x, y + 1] != 255: + count += 1 + if pixels[x + 1, y + 1] != 255: + count += 1 + if pixels[x + 1, y] != 255: + count += 1 + if pixels[x + 1, y - 1] != 255: + count += 1 + if pixels[x, y - 1] != 255: + count += 1 + except Exception: + pass + + # not enough neighbors are dark pixels so mark this pixel + # to be changed to white + if count < allowed: + pixels[x, y] = 1 + + # second pass: this time set all 1's to 255 (white) + for x in xrange(w): + for y in xrange(h): + if pixels[x, y] == 1: + pixels[x, y] = 255 + + self.pixels = pixels + + + def derotate_by_average(self): + """rotate by checking each angle and guess most suitable""" + + w, h = self.image.size + pixels = self.pixels + + for x in xrange(w): + for y in xrange(h): + if pixels[x, y] == 0: + pixels[x, y] = 155 + + highest = {} + counts = {} + + for angle in xrange(-45, 45): + + tmpimage = self.image.rotate(angle) + + pixels = tmpimage.load() + + w, h = self.image.size + + for x in xrange(w): + for y in xrange(h): + if pixels[x, y] == 0: + pixels[x, y] = 255 + + count = {} + + for x in xrange(w): + count[x] = 0 + for y in xrange(h): + if pixels[x, y] == 155: + count[x] += 1 + + sum = 0 + cnt = 0 + + for x in count.values(): + if x != 0: + sum += x + cnt += 1 + + avg = sum / cnt + counts[angle] = cnt + highest[angle] = 0 + for x in count.values(): + if x > highest[angle]: + highest[angle] = x + + highest[angle] = highest[angle] - avg + + hkey = 0 + hvalue = 0 + + for key, value in highest.iteritems(): + if value > hvalue: + hkey = key + hvalue = value + + self.image = self.image.rotate(hkey) + pixels = self.image.load() + + for x in xrange(w): + for y in xrange(h): + if pixels[x, y] == 0: + pixels[x, y] = 255 + + if pixels[x, y] == 155: + pixels[x, y] = 0 + + self.pixels = pixels + + + def split_captcha_letters(self): + captcha = self.image + started = False + letters = [] + width, height = captcha.size + bottomY, topY = 0, height + pixels = captcha.load() + + for x in xrange(width): + black_pixel_in_col = False + for y in xrange(height): + if pixels[x, y] != 255: + if not started: + started = True + firstX = x + lastX = x + + if y > bottomY: + bottomY = y + if y < topY: + topY = y + if x > lastX: + lastX = x + + black_pixel_in_col = True + + if black_pixel_in_col is False and started is True: + rect = (firstX, topY, lastX, bottomY) + new_captcha = captcha.crop(rect) + + w, h = new_captcha.size + if w > 5 and h > 5: + letters.append(new_captcha) + + started = False + bottomY, topY = 0, height + + return letters + + + def correct(self, values, var=None): + if var: + result = var + else: + result = self.result_captcha + + for key, item in values.iteritems(): + + if key.__class__ == str: + result = result.replace(key, item) + else: + for expr in key: + result = result.replace(expr, item) + + if var: + return result + else: + self.result_captcha = result diff --git a/module/plugins/Plugin.py b/module/plugins/Plugin.py new file mode 100644 index 000000000..0a9c647fb --- /dev/null +++ b/module/plugins/Plugin.py @@ -0,0 +1,753 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +from time import time, sleep +from random import randint + +import os +from os import remove, makedirs, chmod, stat +from os.path import exists, join + +if os.name != "nt": + from os import chown + from pwd import getpwnam + from grp import getgrnam + +from itertools import islice +from traceback import print_exc +from urlparse import urlparse + +from pyload.utils import fs_decode, fs_encode, safe_filename, safe_join + + +def chunks(iterable, size): + it = iter(iterable) + item = list(islice(it, size)) + while item: + yield item + item = list(islice(it, size)) + + +class Abort(Exception): + """ raised when aborted """ + + +class Fail(Exception): + """ raised when failed """ + + +class Reconnect(Exception): + """ raised when reconnected """ + + +class Retry(Exception): + """ raised when start again from beginning """ + + +class SkipDownload(Exception): + """ raised when download should be skipped """ + + +class Base(object): + """ + A Base class with log/config/db methods *all* plugin types can use + """ + + def __init__(self, core): + #: Core instance + self.core = core + + + def _log(self, type, args): + msg = " | ".join([encode(a).strip() for a in args if a]) + logger = getattr(self.core.log, type) + logger("%s: %s" % (self.__name__, msg or _("%s MARK" % type.upper()))) + + + def logDebug(self, *args): + if self.core.debug: + return self._log("debug", args) + + + def logInfo(self, *args): + return self._log("info", args) + + + def logWarning(self, *args): + return self._log("warning", args) + + + def logError(self, *args): + return self._log("error", args) + + + def logCritical(self, *args): + return self._log("critical", args) + + + #: Deprecated method + def setConf(self, option, value): + """ see `setConfig` """ + self.setConfig(option, value) + + + def setConfig(self, option, value): + """ Set config value for current plugin + + :param option: + :param value: + :return: + """ + self.core.config.setPlugin(self.__name__, option, value) + + + #: Deprecated method + def getConf(self, option): + """ see `getConfig` """ + return self.getConfig(option) + + + def getConfig(self, option): + """ Returns config value for current plugin + + :param option: + :return: + """ + return self.core.config.getPlugin(self.__name__, option) + + + def setStorage(self, key, value): + """ Saves a value persistently to the database """ + self.core.db.setStorage(self.__name__, key, value) + + + def store(self, key, value): + """ same as `setStorage` """ + self.core.db.setStorage(self.__name__, key, value) + + + def getStorage(self, key=None, default=None): + """ Retrieves saved value or dict of all saved entries if key is None """ + if key: + return self.core.db.getStorage(self.__name__, key) or default + return self.core.db.getStorage(self.__name__, key) + + + def retrieve(self, *args, **kwargs): + """ same as `getStorage` """ + return self.getStorage(*args, **kwargs) + + + def delStorage(self, key): + """ Delete entry in db """ + self.core.db.delStorage(self.__name__, key) + + +class Plugin(Base): + """ + Base plugin for hoster/crypter. + Overwrite `process` / `decrypt` in your subclassed plugin. + """ + __name__ = "Plugin" + __type__ = "hoster" + __version__ = "0.07" + + __pattern__ = r'^unmatchable$' + __config__ = [] #: [("name", "type", "desc", "default")] + + __description__ = """Base plugin""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org"), + ("spoob", "spoob@pyload.org"), + ("mkaay", "mkaay@mkaay.de")] + + + info = {} #: file info dict + + + def __init__(self, pyfile): + Base.__init__(self, pyfile.m.core) + + #: engage wan reconnection + self.wantReconnect = False + + #: enable simultaneous processing of multiple downloads + self.multiDL = True + self.limitDL = 0 + + #: chunk limit + self.chunkLimit = 1 + self.resumeDownload = False + + #: time() + wait in seconds + self.waitUntil = 0 + self.waiting = False + + #: captcha reader instance + self.ocr = None + + #: account handler instance, see :py:class:`Account` + self.account = pyfile.m.core.accountManager.getAccountPlugin(self.__name__) + + #: premium status + self.premium = False + #: username/login + self.user = None + + if self.account and not self.account.canUse(): + self.account = None + + if self.account: + self.user, data = self.account.selectAccount() + #: Browser instance, see `network.Browser` + self.req = self.account.getAccountRequest(self.user) + self.chunkLimit = -1 # chunk limit, -1 for unlimited + #: enables resume (will be ignored if server dont accept chunks) + self.resumeDownload = True + self.multiDL = True #every hoster with account should provide multiple downloads + #: premium status + self.premium = self.account.isPremium(self.user) + else: + self.req = pyfile.m.core.requestFactory.getRequest(self.__name__) + + #: associated pyfile instance, see `PyFile` + self.pyfile = pyfile + + self.thread = None # holds thread in future + + #: location where the last call to download was saved + self.lastDownload = "" + #: re match of the last call to `checkDownload` + self.lastCheck = None + + #: js engine, see `JsEngine` + self.js = self.core.js + + #: captcha task + self.cTask = None + + self.html = None #@TODO: Move to hoster class in 0.4.10 + self.retries = 0 + + self.init() + + + def getChunkCount(self): + if self.chunkLimit <= 0: + return self.core.config['download']['chunks'] + return min(self.core.config['download']['chunks'], self.chunkLimit) + + + def __call__(self): + return self.__name__ + + + def init(self): + """initialize the plugin (in addition to `__init__`)""" + pass + + + def setup(self): + """ setup for enviroment and other things, called before downloading (possibly more than one time)""" + pass + + + def preprocessing(self, thread): + """ handles important things to do before starting """ + self.thread = thread + + if self.account: + self.account.checkLogin(self.user) + else: + self.req.clearCookies() + + self.setup() + + self.pyfile.setStatus("starting") + + return self.process(self.pyfile) + + + def process(self, pyfile): + """the 'main' method of every plugin, you **have to** overwrite it""" + raise NotImplementedError + + + def resetAccount(self): + """ dont use account and retry download """ + self.account = None + self.req = self.core.requestFactory.getRequest(self.__name__) + self.retry() + + + def checksum(self, local_file=None): + """ + return codes: + 0 - checksum ok + 1 - checksum wrong + 5 - can't get checksum + 10 - not implemented + 20 - unknown error + """ + #@TODO checksum check addon + + return True, 10 + + + def setReconnect(self, reconnect): + reconnect = bool(reconnect) + self.logDebug("Set wantReconnect to: %s (previous: %s)" % (reconnect, self.wantReconnect)) + self.wantReconnect = reconnect + + + def setWait(self, seconds, reconnect=None): + """Set a specific wait time later used with `wait` + + :param seconds: wait time in seconds + :param reconnect: True if a reconnect would avoid wait time + """ + wait_time = int(seconds) + 1 + wait_until = time() + wait_time + + self.logDebug("Set waitUntil to: %f (previous: %f)" % (wait_until, self.pyfile.waitUntil), + "Wait: %d seconds" % wait_time) + + self.pyfile.waitUntil = wait_until + + if reconnect is not None: + self.setReconnect(reconnect) + + + def wait(self, seconds=None, reconnect=None): + """ waits the time previously set """ + + pyfile = self.pyfile + + if seconds is not None: + self.setWait(seconds) + + if reconnect is not None: + self.setReconnect(reconnect) + + self.waiting = True + + status = pyfile.status + pyfile.setStatus("waiting") + + self.logInfo(_("Wait: %d seconds") % (pyfile.waitUntil - time()), + _("Reconnect: %s") % self.wantReconnect) + + if self.account: + self.logDebug("Ignore reconnection due account logged") + + while pyfile.waitUntil > time(): + if pyfile.abort: + self.abort() + + sleep(1) + else: + while pyfile.waitUntil > time(): + self.thread.m.reconnecting.wait(2) + + if pyfile.abort: + self.abort() + + if self.thread.m.reconnecting.isSet(): + self.waiting = False + self.wantReconnect = False + raise Reconnect + + sleep(1) + + self.waiting = False + + pyfile.status = status + + + def fail(self, reason): + """ fail and give reason """ + raise Fail(reason) + + + def abort(self, reason=""): + """ abort and give reason """ + if reason: + self.pyfile.error = str(reason) + raise Abort + + + def error(self, reason="", type=""): + if not reason and not type: + type = "unknown" + + msg = _("%s error") % _(type.strip().capitalize()) if type else _("Error") + msg += ": " + reason.strip() if reason else "" + msg += _(" | Plugin may be out of date") + + raise Fail(msg) + + + def offline(self, reason=""): + """ fail and indicate file is offline """ + if reason: + self.pyfile.error = str(reason) + raise Fail("offline") + + + def tempOffline(self, reason=""): + """ fail and indicates file ist temporary offline, the core may take consequences """ + if reason: + self.pyfile.error = str(reason) + raise Fail("temp. offline") + + + def retry(self, max_tries=5, wait_time=1, reason=""): + """Retries and begin again from the beginning + + :param max_tries: number of maximum retries + :param wait_time: time to wait in seconds + :param reason: reason for retrying, will be passed to fail if max_tries reached + """ + if 0 < max_tries <= self.retries: + self.error(reason or _("Max retries reached"), "retry") + + self.wait(wait_time, False) + + self.retries += 1 + raise Retry(reason) + + + def invalidCaptcha(self): + self.logError(_("Invalid captcha")) + if self.cTask: + self.cTask.invalid() + + + def correctCaptcha(self): + self.logInfo(_("Correct captcha")) + if self.cTask: + self.cTask.correct() + + + def decryptCaptcha(self, url, get={}, post={}, cookies=False, forceUser=False, imgtype='jpg', + result_type='textual', timeout=290): + """ Loads a captcha and decrypts it with ocr, plugin, user input + + :param url: url of captcha image + :param get: get part for request + :param post: post part for request + :param cookies: True if cookies should be enabled + :param forceUser: if True, ocr is not used + :param imgtype: Type of the Image + :param result_type: 'textual' if text is written on the captcha\ + or 'positional' for captcha where the user have to click\ + on a specific region on the captcha + + :return: result of decrypting + """ + + img = self.load(url, get=get, post=post, cookies=cookies) + + id = ("%.2f" % time())[-6:].replace(".", "") + + with open(join("tmp", "tmpCaptcha_%s_%s.%s" % (self.__name__, id, imgtype)), "wb") as tmpCaptcha: + tmpCaptcha.write(img) + + has_plugin = self.__name__ in self.core.pluginManager.ocrPlugins + + if self.core.captcha: + Ocr = self.core.pluginManager.loadClass("ocr", self.__name__) + else: + Ocr = None + + if Ocr and not forceUser: + sleep(randint(3000, 5000) / 1000.0) + if self.pyfile.abort: + self.abort() + + ocr = Ocr() + result = ocr.get_captcha(tmpCaptcha.name) + else: + captchaManager = self.core.captchaManager + task = captchaManager.newTask(img, imgtype, tmpCaptcha.name, result_type) + self.cTask = task + captchaManager.handleCaptcha(task, timeout) + + while task.isWaiting(): + if self.pyfile.abort: + captchaManager.removeTask(task) + self.abort() + sleep(1) + + captchaManager.removeTask(task) + + if task.error and has_plugin: #ignore default error message since the user could use OCR + self.fail(_("Pil and tesseract not installed and no Client connected for captcha decrypting")) + elif task.error: + self.fail(task.error) + elif not task.result: + self.fail(_("No captcha result obtained in appropiate time by any of the plugins")) + + result = task.result + self.logDebug("Received captcha result: %s" % result) + + if not self.core.debug: + try: + remove(tmpCaptcha.name) + except Exception: + pass + + return result + + + def load(self, url, get={}, post={}, ref=True, cookies=True, just_header=False, decode=False, follow_location=True, save_cookies=True): + """Load content at url and returns it + + :param url: + :param get: + :param post: + :param ref: + :param cookies: + :param just_header: If True only the header will be retrieved and returned as dict + :param decode: Wether to decode the output according to http header, should be True in most cases + :param follow_location: If True follow location else not + :param save_cookies: If True saves received cookies else discard them + :return: Loaded content + """ + if self.pyfile.abort: + self.abort() + + if not url: + self.fail(_("No url given")) + + url = encode(url).strip() #@NOTE: utf8 vs decode -> please use decode attribute in all future plugins + + if self.core.debug: + self.logDebug("Load url: " + url, *["%s=%s" % (key, val) for key, val in locals().iteritems() if key not in ("self", "url")]) + + res = self.req.load(url, get, post, ref, cookies, just_header, decode=decode, follow_location=follow_location, save_cookies=save_cookies) + + if decode: + res = encode(res) + + if self.core.debug: + from inspect import currentframe + + frame = currentframe() + framefile = safe_join("tmp", self.__name__, "%s_line%s.dump.html" % (frame.f_back.f_code.co_name, frame.f_back.f_lineno)) + try: + if not exists(join("tmp", self.__name__)): + makedirs(join("tmp", self.__name__)) + + with open(framefile, "wb") as f: + del frame #: delete the frame or it wont be cleaned + f.write(res) + except IOError, e: + self.logError(e) + + if just_header: + #parse header + header = {"code": self.req.code} + for line in res.splitlines(): + line = line.strip() + if not line or ":" not in line: continue + + key, none, value = line.partition(":") + key = key.strip().lower() + value = value.strip() + + if key in header: + if type(header[key]) == list: + header[key].append(value) + else: + header[key] = [header[key], value] + else: + header[key] = value + res = header + + return res + + + def download(self, url, get={}, post={}, ref=True, cookies=True, disposition=False): + """Downloads the content at url to download folder + + :param url: + :param get: + :param post: + :param ref: + :param cookies: + :param disposition: if True and server provides content-disposition header\ + the filename will be changed if needed + :return: The location where the file was saved + """ + if self.pyfile.abort: + self.abort() + + if not url: + self.fail(_("No url given")) + + url = encode(url).strip() + + if self.core.debug: + self.logDebug("Download url: " + url, *["%s=%s" % (key, val) for key, val in locals().iteritems() if key not in ("self", "url")]) + + self.checkForSameFiles() + + self.pyfile.setStatus("downloading") + + download_folder = self.core.config['general']['download_folder'] + + location = safe_join(download_folder, self.pyfile.package().folder) + + if not exists(location): + try: + makedirs(location, int(self.core.config['permission']['folder'], 8)) + + if self.core.config['permission']['change_dl'] and os.name != "nt": + uid = getpwnam(self.core.config['permission']['user'])[2] + gid = getgrnam(self.core.config['permission']['group'])[2] + chown(location, uid, gid) + + except Exception, e: + self.fail(e) + + # convert back to unicode + location = fs_decode(location) + name = safe_filename(self.pyfile.name) + + filename = join(location, name) + + self.core.addonManager.dispatchEvent("download-start", self.pyfile, url, filename) + + try: + newname = self.req.httpDownload(url, filename, get=get, post=post, ref=ref, cookies=cookies, + chunks=self.getChunkCount(), resume=self.resumeDownload, + progressNotify=self.pyfile.setProgress, disposition=disposition) + finally: + self.pyfile.size = self.req.size + + if newname: + newname = urlparse(newname).path.split("/")[-1] + + if disposition and newname != name: + self.logInfo(_("%(name)s saved as %(newname)s") % {"name": name, "newname": newname}) + self.pyfile.name = newname + filename = join(location, newname) + + fs_filename = fs_encode(filename) + + if self.core.config['permission']['change_file']: + try: + chmod(fs_filename, int(self.core.config['permission']['file'], 8)) + except Exception, e: + self.logWarning(_("Setting file mode failed"), e) + + if self.core.config['permission']['change_dl'] and os.name != "nt": + try: + uid = getpwnam(self.core.config['permission']['user'])[2] + gid = getgrnam(self.core.config['permission']['group'])[2] + chown(fs_filename, uid, gid) + + except Exception, e: + self.logWarning(_("Setting User and Group failed"), e) + + self.lastDownload = filename + return self.lastDownload + + + def checkDownload(self, rules, api_size=0, max_size=50000, delete=True, read_size=0): + """ checks the content of the last downloaded file, re match is saved to `lastCheck` + + :param rules: dict with names and rules to match (compiled regexp or strings) + :param api_size: expected file size + :param max_size: if the file is larger then it wont be checked + :param delete: delete if matched + :param read_size: amount of bytes to read from files larger then max_size + :return: dictionary key of the first rule that matched + """ + lastDownload = fs_encode(self.lastDownload) + if not exists(lastDownload): + return None + + size = stat(lastDownload) + size = size.st_size + + if api_size and api_size <= size: return None + elif size > max_size and not read_size: return None + self.logDebug("Download Check triggered") + + with open(lastDownload, "rb") as f: + content = f.read(read_size if read_size else -1) + + #produces encoding errors, better log to other file in the future? + #self.logDebug("Content: %s" % content) + for name, rule in rules.iteritems(): + if isinstance(rule, basestring): + if rule in content: + if delete: + remove(lastDownload) + return name + elif hasattr(rule, "search"): + m = rule.search(content) + if m: + if delete: + remove(lastDownload) + self.lastCheck = m + return name + + + def getPassword(self): + """ get the password the user provided in the package""" + password = self.pyfile.package().password + if not password: return "" + return password + + + def checkForSameFiles(self, starting=False): + """ checks if same file was/is downloaded within same package + + :param starting: indicates that the current download is going to start + :raises SkipDownload: + """ + + pack = self.pyfile.package() + + for pyfile in self.core.files.cache.values(): + if pyfile != self.pyfile and pyfile.name == self.pyfile.name and pyfile.package().folder == pack.folder: + if pyfile.status in (0, 12): #finished or downloading + raise SkipDownload(pyfile.pluginname) + elif pyfile.status in ( + 5, 7) and starting: #a download is waiting/starting and was appenrently started before + raise SkipDownload(pyfile.pluginname) + + download_folder = self.core.config['general']['download_folder'] + location = safe_join(download_folder, pack.folder, self.pyfile.name) + + if starting and self.core.config['download']['skip_existing'] and exists(location): + size = os.stat(location).st_size + if size >= self.pyfile.size: + raise SkipDownload("File exists") + + pyfile = self.core.db.findDuplicates(self.pyfile.id, self.pyfile.package().folder, self.pyfile.name) + if pyfile: + if exists(location): + raise SkipDownload(pyfile[0]) + + self.logDebug("File %s not skipped, because it does not exists." % self.pyfile.name) + + + def clean(self): + """ clean everything and remove references """ + if hasattr(self, "pyfile"): + del self.pyfile + + if hasattr(self, "req"): + self.req.close() + del self.req + + if hasattr(self, "thread"): + del self.thread + + if hasattr(self, "html"): + del self.html diff --git a/module/plugins/__init__.py b/module/plugins/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/module/plugins/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/module/plugins/accounts/AlldebridCom.py b/module/plugins/accounts/AlldebridCom.py new file mode 100644 index 000000000..f9906bd9d --- /dev/null +++ b/module/plugins/accounts/AlldebridCom.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +import re +import xml.dom.minidom as dom + +from time import time +from urllib import urlencode + +from BeautifulSoup import BeautifulSoup + +from pyload.plugin.Account import Account + + +class AlldebridCom(Account): + __name__ = "AlldebridCom" + __type__ = "account" + __version__ = "0.22" + + __description__ = """AllDebrid.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("Andy Voigt", "spamsales@online.de")] + + + def loadAccountInfo(self, user, req): + data = self.getAccountData(user) + page = req.load("http://www.alldebrid.com/account/") + soup = BeautifulSoup(page) + #Try to parse expiration date directly from the control panel page (better accuracy) + try: + time_text = soup.find('div', attrs={'class': 'remaining_time_text'}).strong.string + self.logDebug("Account expires in: %s" % time_text) + p = re.compile('\d+') + exp_data = p.findall(time_text) + exp_time = time() + int(exp_data[0]) * 24 * 60 * 60 + int( + exp_data[1]) * 60 * 60 + (int(exp_data[2]) - 1) * 60 + #Get expiration date from API + except Exception: + data = self.getAccountData(user) + page = req.load("http://www.alldebrid.com/api.php", + get={'action': "info_user", 'login': user, 'pw': data['password']}) + self.logDebug(page) + xml = dom.parseString(page) + exp_time = time() + int(xml.getElementsByTagName("date")[0].childNodes[0].nodeValue) * 24 * 60 * 60 + account_info = {"validuntil": exp_time, "trafficleft": -1} + return account_info + + + def login(self, user, data, req): + urlparams = urlencode({'action': 'login', 'login_login': user, 'login_password': data['password']}) + page = req.load("http://www.alldebrid.com/register/?%s" % urlparams) + + if "This login doesn't exist" in page: + self.wrongPassword() + + if "The password is not valid" in page: + self.wrongPassword() + + if "Invalid captcha" in page: + self.wrongPassword() diff --git a/module/plugins/accounts/BayfilesCom.py b/module/plugins/accounts/BayfilesCom.py new file mode 100644 index 000000000..760c29d4e --- /dev/null +++ b/module/plugins/accounts/BayfilesCom.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- + +from time import time + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class BayfilesCom(Account): + __name__ = "BayfilesCom" + __type__ = "account" + __version__ = "0.03" + + __description__ = """Bayfiles.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + def loadAccountInfo(self, user, req): + for _i in xrange(2): + res = json_loads(req.load("http://api.bayfiles.com/v1/account/info")) + self.logDebug(res) + if not res['error']: + break + self.logWarning(res['error']) + self.relogin(user) + + return {"premium": bool(res['premium']), "trafficleft": -1, + "validuntil": res['expires'] if res['expires'] >= int(time()) else -1} + + + def login(self, user, data, req): + res = json_loads(req.load("http://api.bayfiles.com/v1/account/login/%s/%s" % (user, data['password']))) + self.logDebug(res) + if res['error']: + self.logError(res['error']) + self.wrongPassword() diff --git a/module/plugins/accounts/BillionuploadsCom.py b/module/plugins/accounts/BillionuploadsCom.py new file mode 100644 index 000000000..a3325c427 --- /dev/null +++ b/module/plugins/accounts/BillionuploadsCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class BillionuploadsCom(XFSAccount): + __name__ = "BillionuploadsCom" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Billionuploads.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + HOSTER_DOMAIN = "billionuploads.com" diff --git a/module/plugins/accounts/BitshareCom.py b/module/plugins/accounts/BitshareCom.py new file mode 100644 index 000000000..aabab9e19 --- /dev/null +++ b/module/plugins/accounts/BitshareCom.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + + +class BitshareCom(Account): + __name__ = "BitshareCom" + __type__ = "account" + __version__ = "0.12" + + __description__ = """Bitshare account plugin""" + __license__ = "GPLv3" + __authors__ = [("Paul King", "")] + + + def loadAccountInfo(self, user, req): + page = req.load("http://bitshare.com/mysettings.html") + + if "\"http://bitshare.com/myupgrade.html\">Free" in page: + return {"validuntil": -1, "trafficleft": -1, "premium": False} + + if not '' in page: + self.logWarning(_("Activate direct Download in your Bitshare Account")) + + return {"validuntil": -1, "trafficleft": -1, "premium": True} + + + def login(self, user, data, req): + page = req.load("http://bitshare.com/login.html", + post={"user": user, "password": data['password'], "submit": "Login"}, cookies=True) + if "login" in req.lastEffectiveURL: + self.wrongPassword() diff --git a/module/plugins/accounts/CatShareNet.py b/module/plugins/accounts/CatShareNet.py new file mode 100644 index 000000000..4c1cd00d7 --- /dev/null +++ b/module/plugins/accounts/CatShareNet.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +import re + +from time import mktime, strptime + +from pyload.plugin.Account import Account + + +class CatShareNet(Account): + __name__ = "CatShareNet" + __type__ = "account" + __version__ = "0.01" + + __description__ = """CatShareNet account plugin""" + __license__ = "GPLv3" + __authors__ = [("prOq", "")] + + + PREMIUM_PATTERN = r'class="nav-collapse collapse pull-right">[\s\w<>=-."/:]*\sz.\s*
  • .*\s*(.*?)[\s\w<>/]*href="/logout"' + VALID_UNTIL_PATTERN = r'
    [\s\w<>=-":;]*.*?(.*?)' + + + def loadAccountInfo(self, user, req): + premium = False + validuntil = -1 + + html = req.load("http://catshare.net/", decode=True) + + try: + m = re.search(self.PREMIUM_PATTERN, html) + if "Premium" in m.group(1): + premium = True + except Exception: + pass + + try: + m = re.search(self.VALID_UNTIL_PATTERN, html) + expiredate = m.group(1) + if "-" not in expiredate: + validuntil = mktime(strptime(expiredate, "%d.%m.%Y")) + except Exception: + pass + + return {'premium': premium, 'trafficleft': -1, 'validuntil': validuntil} + + + def login(self, user, data, req): + html = req.load("http://catshare.net/login", + post={'user_email': user, + 'user_password': data['password'], + 'remindPassword': 0, + 'user[submit]': "Login"}) + + if not 'Wyloguj' in html: + self.wrongPassword() diff --git a/module/plugins/accounts/CramitIn.py b/module/plugins/accounts/CramitIn.py new file mode 100644 index 000000000..21503f625 --- /dev/null +++ b/module/plugins/accounts/CramitIn.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class CramitIn(XFSAccount): + __name__ = "CramitIn" + __type__ = "account" + __version__ = "0.03" + + __description__ = """Cramit.in account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + HOSTER_DOMAIN = "cramit.in" diff --git a/module/plugins/accounts/CzshareCom.py b/module/plugins/accounts/CzshareCom.py new file mode 100644 index 000000000..6608efd90 --- /dev/null +++ b/module/plugins/accounts/CzshareCom.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +from time import mktime, strptime +import re + +from pyload.plugin.Account import Account + + +class CzshareCom(Account): + __name__ = "CzshareCom" + __type__ = "account" + __version__ = "0.14" + + __description__ = """Czshare.com account plugin, now Sdilej.cz""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), + ("stickell", "l.stickell@yahoo.it")] + + + CREDIT_LEFT_PATTERN = r'\s*([\d ,]+) (KiB|MiB|GiB)\s*([^<]*)\s*' + + + def loadAccountInfo(self, user, req): + html = req.load("http://sdilej.cz/prehled_kreditu/") + + m = re.search(self.CREDIT_LEFT_PATTERN, html) + if m is None: + return {"validuntil": 0, "trafficleft": 0} + else: + credits = float(m.group(1).replace(' ', '').replace(',', '.')) + credits = credits * 1024 ** {'KiB': 0, 'MiB': 1, 'GiB': 2}[m.group(2)] + validuntil = mktime(strptime(m.group(3), '%d.%m.%y %H:%M')) + return {"validuntil": validuntil, "trafficleft": credits} + + + def login(self, user, data, req): + html = req.load('https://sdilej.cz/index.php', post={ + "Prihlasit": "Prihlasit", + "login-password": data['password'], + "login-name": user + }) + + if '", html).group(1) + + validuntil = int(mktime(strptime(validuntil, "%Y-%m-%d %H:%M:%S"))) + + return {"validuntil": validuntil, "trafficleft": -1} + + + def login(self, user, data, req): + html = req.load("https://dfiles.eu/de/login.php", get={"return": "/de/gold/payment.php"}, + post={"login": user, "password": data['password']}) + if r'
    Sie haben eine falsche Benutzername-Passwort-Kombination verwendet.
    ' in html: + self.wrongPassword() diff --git a/module/plugins/accounts/DropboxCom.py b/module/plugins/accounts/DropboxCom.py new file mode 100644 index 000000000..341bb761d --- /dev/null +++ b/module/plugins/accounts/DropboxCom.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster, create_getInfo + + +class DropboxCom(SimpleHoster): + __name__ = "DropboxCom" + __type__ = "hoster" + __version__ = "0.03" + + __pattern__ = r'https?://(?:www\.)?dropbox\.com/.+' + + __description__ = """Dropbox.com hoster plugin""" + __license__ = "GPLv3" + __authors__ = [("zapp-brannigan", "fuerst.reinje@web.de")] + + + NAME_PATTERN = r'Dropbox - (?P<N>.+?)<' + SIZE_PATTERN = r' ·  (?P<S>[\d.,]+) (?P<U>[\w^_]+)' + + OFFLINE_PATTERN = r'<title>Dropbox - (404|Shared link error)<' + + COOKIES = [("dropbox.com", "lang", "en")] + + + def setup(self): + self.multiDL = True + self.chunkLimit = 1 + self.resumeDownload = True + + + def handleFree(self): + self.download(self.pyfile.url, get={'dl': "1"}) + + check = self.checkDownload({'html': re.compile("html")}) + if check == "html": + self.error(_("Downloaded file is an html page")) + + +getInfo = create_getInfo(DropboxCom) diff --git a/module/plugins/accounts/EasybytezCom.py b/module/plugins/accounts/EasybytezCom.py new file mode 100644 index 000000000..c7d717474 --- /dev/null +++ b/module/plugins/accounts/EasybytezCom.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class EasybytezCom(XFSAccount): + __name__ = "EasybytezCom" + __type__ = "account" + __version__ = "0.12" + + __description__ = """EasyBytez.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), + ("guidobelix", "guidobelix@hotmail.it")] + + + HOSTER_DOMAIN = "easybytez.com" diff --git a/module/plugins/accounts/EuroshareEu.py b/module/plugins/accounts/EuroshareEu.py new file mode 100644 index 000000000..2d9288ba8 --- /dev/null +++ b/module/plugins/accounts/EuroshareEu.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +from time import mktime, strptime +import re + +from pyload.plugin.Account import Account + + +class EuroshareEu(Account): + __name__ = "EuroshareEu" + __type__ = "account" + __version__ = "0.01" + + __description__ = """Euroshare.eu account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + def loadAccountInfo(self, user, req): + self.relogin(user) + html = req.load("http://euroshare.eu/customer-zone/settings/") + + m = re.search('id="input_expire_date" value="(\d+\.\d+\.\d+ \d+:\d+)"', html) + if m is None: + premium, validuntil = False, -1 + else: + premium = True + validuntil = mktime(strptime(m.group(1), "%d.%m.%Y %H:%M")) + + return {"validuntil": validuntil, "trafficleft": -1, "premium": premium} + + + def login(self, user, data, req): + html = req.load('http://euroshare.eu/customer-zone/login/', post={ + "trvale": "1", + "login": user, + "password": data['password'] + }, decode=True) + + if u">Nesprávne prihlasovacie meno alebo heslo" in html: + self.wrongPassword() diff --git a/module/plugins/accounts/FastixRu.py b/module/plugins/accounts/FastixRu.py new file mode 100644 index 000000000..0f2ac71ce --- /dev/null +++ b/module/plugins/accounts/FastixRu.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class FastixRu(Account): + __name__ = "FastixRu" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Fastix account plugin""" + __license__ = "GPLv3" + __authors__ = [("Massimo Rosamilia", "max@spiritix.eu")] + + + def loadAccountInfo(self, user, req): + data = self.getAccountData(user) + page = json_loads(req.load("http://fastix.ru/api_v2/", get={'apikey': data['api'], 'sub': "getaccountdetails"})) + + points = page['points'] + kb = float(points) * 1024 ** 2 / 1000 + + if points > 0: + account_info = {"validuntil": -1, "trafficleft": kb} + else: + account_info = {"validuntil": None, "trafficleft": None, "premium": False} + return account_info + + + def login(self, user, data, req): + page = req.load("http://fastix.ru/api_v2/", + get={'sub': "get_apikey", 'email': user, 'password': data['password']}) + api = json_loads(page) + api = api['apikey'] + data['api'] = api + if "error_code" in page: + self.wrongPassword() diff --git a/module/plugins/accounts/FastshareCz.py b/module/plugins/accounts/FastshareCz.py new file mode 100644 index 000000000..8fe98438b --- /dev/null +++ b/module/plugins/accounts/FastshareCz.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Account import Account +from pyload.utils import parseFileSize + + +class FastshareCz(Account): + __name__ = "FastshareCz" + __type__ = "account" + __version__ = "0.05" + + __description__ = """Fastshare.cz account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), + ("stickell", "l.stickell@yahoo.it")] + + + CREDIT_PATTERN = r'My account\s*\((.+?)\)' + + + def loadAccountInfo(self, user, req): + validuntil = None + trafficleft = None + premium = None + + html = req.load("http://www.fastshare.cz/user", decode=True) + + m = re.search(self.CREDIT_PATTERN, html) + if m: + trafficleft = self.parseTraffic(m.group(1)) + + if trafficleft: + premium = True + validuntil = -1 + else: + premium = False + + return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium} + + + def login(self, user, data, req): + req.cj.setCookie("fastshare.cz", "lang", "en") + + req.load('http://www.fastshare.cz/login') # Do not remove or it will not login + + html = req.load("http://www.fastshare.cz/sql.php", + post={'login': user, 'heslo': data['password']}, + decode=True) + + if ">Wrong username or password" in html: + self.wrongPassword() diff --git a/module/plugins/accounts/File4SafeCom.py b/module/plugins/accounts/File4SafeCom.py new file mode 100644 index 000000000..4f311aa4c --- /dev/null +++ b/module/plugins/accounts/File4SafeCom.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class File4safeCom(XFSAccount): + __name__ = "File4safeCom" + __type__ = "account" + __version__ = "0.04" + + __description__ = """File4safe.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("stickell", "l.stickell@yahoo.it")] + + + HOSTER_DOMAIN = "file4safe.com" + + LOGIN_FAIL_PATTERN = r'input_login' diff --git a/module/plugins/accounts/FileParadoxIn.py b/module/plugins/accounts/FileParadoxIn.py new file mode 100644 index 000000000..02b923519 --- /dev/null +++ b/module/plugins/accounts/FileParadoxIn.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class FileParadoxIn(XFSAccount): + __name__ = "FileParadoxIn" + __type__ = "account" + __version__ = "0.02" + + __description__ = """FileParadox.in account plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + HOSTER_DOMAIN = "fileparadox.in" diff --git a/module/plugins/accounts/FilecloudIo.py b/module/plugins/accounts/FilecloudIo.py new file mode 100644 index 000000000..8169bfdce --- /dev/null +++ b/module/plugins/accounts/FilecloudIo.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class FilecloudIo(Account): + __name__ = "FilecloudIo" + __type__ = "account" + __version__ = "0.02" + + __description__ = """FilecloudIo account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), + ("stickell", "l.stickell@yahoo.it")] + + + def loadAccountInfo(self, user, req): + # It looks like the first API request always fails, so we retry 5 times, it should work on the second try + for _i in xrange(5): + rep = req.load("https://secure.filecloud.io/api-fetch_apikey.api", + post={"username": user, "password": self.accounts[user]['password']}) + rep = json_loads(rep) + if rep['status'] == 'ok': + break + elif rep['status'] == 'error' and rep['message'] == 'no such user or wrong password': + self.logError(_("Wrong username or password")) + return {"valid": False, "premium": False} + else: + return {"premium": False} + + akey = rep['akey'] + self.accounts[user]['akey'] = akey # Saved for hoster plugin + rep = req.load("http://api.filecloud.io/api-fetch_account_details.api", + post={"akey": akey}) + rep = json_loads(rep) + + if rep['is_premium'] == 1: + return {"validuntil": int(rep['premium_until']), "trafficleft": -1} + else: + return {"premium": False} + + + def login(self, user, data, req): + req.cj.setCookie("secure.filecloud.io", "lang", "en") + html = req.load('https://secure.filecloud.io/user-login.html') + + if not hasattr(self, "form_data"): + self.form_data = {} + + self.form_data['username'] = user + self.form_data['password'] = data['password'] + + html = req.load('https://secure.filecloud.io/user-login_p.html', + post=self.form_data, + multipart=True) + + self.logged_in = True if "you have successfully logged in - filecloud.io" in html else False + self.form_data = {} diff --git a/module/plugins/accounts/FilefactoryCom.py b/module/plugins/accounts/FilefactoryCom.py new file mode 100644 index 000000000..6b7399db6 --- /dev/null +++ b/module/plugins/accounts/FilefactoryCom.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +import re +from time import mktime, strptime + +from pycurl import REFERER + +from pyload.plugin.Account import Account + + +class FilefactoryCom(Account): + __name__ = "FilefactoryCom" + __type__ = "account" + __version__ = "0.14" + + __description__ = """Filefactory.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), + ("stickell", "l.stickell@yahoo.it")] + + + VALID_UNTIL_PATTERN = r'Premium valid until: <strong>(?P<d>\d{1,2})\w{1,2} (?P<m>\w{3}), (?P<y>\d{4})</strong>' + + + def loadAccountInfo(self, user, req): + html = req.load("http://www.filefactory.com/account/") + + m = re.search(self.VALID_UNTIL_PATTERN, html) + if m: + premium = True + validuntil = re.sub(self.VALID_UNTIL_PATTERN, '\g<d> \g<m> \g<y>', m.group(0)) + validuntil = mktime(strptime(validuntil, "%d %b %Y")) + else: + premium = False + validuntil = -1 + + return {"premium": premium, "trafficleft": -1, "validuntil": validuntil} + + + def login(self, user, data, req): + req.http.c.setopt(REFERER, "http://www.filefactory.com/member/login.php") + + html = req.load("http://www.filefactory.com/member/signin.php", post={ + "loginEmail": user, + "loginPassword": data['password'], + "Submit": "Sign In"}) + + if req.lastEffectiveURL != "http://www.filefactory.com/account/": + self.wrongPassword() diff --git a/module/plugins/accounts/FilejungleCom.py b/module/plugins/accounts/FilejungleCom.py new file mode 100644 index 000000000..5734f1638 --- /dev/null +++ b/module/plugins/accounts/FilejungleCom.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +import re +from time import mktime, strptime + +from pyload.plugin.Account import Account + + +class FilejungleCom(Account): + __name__ = "FilejungleCom" + __type__ = "account" + __version__ = "0.11" + + __description__ = """Filejungle.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + login_timeout = 60 + + URL = "http://filejungle.com/" + TRAFFIC_LEFT_PATTERN = r'"/extend_premium\.php">Until (\d+ \w+ \d+)<br' + LOGIN_FAILED_PATTERN = r'<span htmlfor="loginUser(Name|Password)" generated="true" class="fail_info">' + + + def loadAccountInfo(self, user, req): + html = req.load(self.URL + "dashboard.php") + m = re.search(self.TRAFFIC_LEFT_PATTERN, html) + if m: + premium = True + validuntil = mktime(strptime(m.group(1), "%d %b %Y")) + else: + premium = False + validuntil = -1 + + return {"premium": premium, "trafficleft": -1, "validuntil": validuntil} + + + def login(self, user, data, req): + html = req.load(self.URL + "login.php", post={ + "loginUserName": user, + "loginUserPassword": data['password'], + "loginFormSubmit": "Login", + "recaptcha_challenge_field": "", + "recaptcha_response_field": "", + "recaptcha_shortencode_field": ""}) + + if re.search(self.LOGIN_FAILED_PATTERN, html): + self.wrongPassword() diff --git a/module/plugins/accounts/FileomCom.py b/module/plugins/accounts/FileomCom.py new file mode 100644 index 000000000..36a11e411 --- /dev/null +++ b/module/plugins/accounts/FileomCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class FileomCom(XFSAccount): + __name__ = "FileomCom" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Fileom.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + HOSTER_DOMAIN = "fileom.com" diff --git a/module/plugins/accounts/FilerNet.py b/module/plugins/accounts/FilerNet.py new file mode 100644 index 000000000..679a9dc40 --- /dev/null +++ b/module/plugins/accounts/FilerNet.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class FilerNet(Account): + __name__ = "FilerNet" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Filer.net account plugin""" + __license__ = "GPLv3" + __authors__ = [("stickell", "l.stickell@yahoo.it")] + + + TOKEN_PATTERN = r'_csrf_token" value="([^"]+)" />' + WALID_UNTIL_PATTERN = r'Der Premium-Zugang ist gültig bis (.+)\.\s*</td>' + TRAFFIC_PATTERN = r'Traffic</th>\s*<td>([^<]+)</td>' + FREE_PATTERN = r'Account Status</th>\s*<td>\s*Free' + + + def loadAccountInfo(self, user, req): + html = req.load("https://filer.net/profile") + + # Free user + if re.search(self.FREE_PATTERN, html): + return {"premium": False, "validuntil": None, "trafficleft": None} + + until = re.search(self.WALID_UNTIL_PATTERN, html) + traffic = re.search(self.TRAFFIC_PATTERN, html) + if until and traffic: + validuntil = int(time.mktime(time.strptime(until.group(1), "%d.%m.%Y %H:%M:%S"))) + trafficleft = self.parseTraffic(traffic.group(1)) + return {"premium": True, "validuntil": validuntil, "trafficleft": trafficleft} + else: + self.logError(_("Unable to retrieve account information")) + return {"premium": False, "validuntil": None, "trafficleft": None} + + + def login(self, user, data, req): + html = req.load("https://filer.net/login") + token = re.search(self.TOKEN_PATTERN, html).group(1) + html = req.load("https://filer.net/login_check", + post={"_username": user, "_password": data['password'], + "_remember_me": "on", "_csrf_token": token, "_target_path": "https://filer.net/"}) + if 'Logout' not in html: + self.wrongPassword() diff --git a/module/plugins/accounts/FilerioCom.py b/module/plugins/accounts/FilerioCom.py new file mode 100644 index 000000000..1d9f8744b --- /dev/null +++ b/module/plugins/accounts/FilerioCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class FilerioCom(XFSAccount): + __name__ = "FilerioCom" + __type__ = "account" + __version__ = "0.03" + + __description__ = """FileRio.in account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + HOSTER_DOMAIN = "filerio.in" diff --git a/module/plugins/accounts/FilesMailRu.py b/module/plugins/accounts/FilesMailRu.py new file mode 100644 index 000000000..bbff7311c --- /dev/null +++ b/module/plugins/accounts/FilesMailRu.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + + +class FilesMailRu(Account): + __name__ = "FilesMailRu" + __type__ = "account" + __version__ = "0.10" + + __description__ = """Filesmail.ru account plugin""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org")] + + + def loadAccountInfo(self, user, req): + return {"validuntil": None, "trafficleft": None} + + + def login(self, user, data, req): + user, domain = user.split("@") + + page = req.load("http://swa.mail.ru/cgi-bin/auth", None, + {"Domain": domain, "Login": user, "Password": data['password'], + "Page": "http://files.mail.ru/"}, cookies=True) + + if "Неверное имя пользователя или пароль" in page: # @TODO seems not to work + self.wrongPassword() diff --git a/module/plugins/accounts/FileserveCom.py b/module/plugins/accounts/FileserveCom.py new file mode 100644 index 000000000..7557450be --- /dev/null +++ b/module/plugins/accounts/FileserveCom.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +from time import mktime, strptime + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class FileserveCom(Account): + __name__ = "FileserveCom" + __type__ = "account" + __version__ = "0.20" + + __description__ = """Fileserve.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("mkaay", "mkaay@mkaay.de")] + + + def loadAccountInfo(self, user, req): + data = self.getAccountData(user) + + page = req.load("http://app.fileserve.com/api/login/", post={"username": user, "password": data['password'], + "submit": "Submit+Query"}) + res = json_loads(page) + + if res['type'] == "premium": + validuntil = mktime(strptime(res['expireTime'], "%Y-%m-%d %H:%M:%S")) + return {"trafficleft": res['traffic'], "validuntil": validuntil} + else: + return {"premium": False, "trafficleft": None, "validuntil": None} + + + def login(self, user, data, req): + page = req.load("http://app.fileserve.com/api/login/", post={"username": user, "password": data['password'], + "submit": "Submit+Query"}) + res = json_loads(page) + + if not res['type']: + self.wrongPassword() + + #login at fileserv page + req.load("http://www.fileserve.com/login.php", + post={"loginUserName": user, "loginUserPassword": data['password'], "autoLogin": "checked", + "loginFormSubmit": "Login"}) diff --git a/module/plugins/accounts/FourSharedCom.py b/module/plugins/accounts/FourSharedCom.py new file mode 100644 index 000000000..3ef865a91 --- /dev/null +++ b/module/plugins/accounts/FourSharedCom.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class FourSharedCom(Account): + __name__ = "FourSharedCom" + __type__ = "account" + __version__ = "0.03" + + __description__ = """FourShared.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), + ("stickell", "l.stickell@yahoo.it")] + + + def loadAccountInfo(self, user, req): + # Free mode only for now + return {"premium": False} + + + def login(self, user, data, req): + req.cj.setCookie("4shared.com", "4langcookie", "en") + res = req.load('http://www.4shared.com/web/login', + post={'login': user, + 'password': data['password'], + 'remember': "on", + '_remember': "on", + 'returnTo': "http://www.4shared.com/account/home.jsp"}) + + if 'Please log in to access your 4shared account' in res: + self.wrongPassword() diff --git a/module/plugins/accounts/FreakshareCom.py b/module/plugins/accounts/FreakshareCom.py new file mode 100644 index 000000000..9905e3f55 --- /dev/null +++ b/module/plugins/accounts/FreakshareCom.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +import re + +from time import strptime, mktime + +from pyload.plugin.Account import Account + + +class FreakshareCom(Account): + __name__ = "FreakshareCom" + __type__ = "account" + __version__ = "0.11" + + __description__ = """Freakshare.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org")] + + + def loadAccountInfo(self, user, req): + page = req.load("http://freakshare.com/") + + validuntil = r'ltig bis:</td>\s*<td><b>([\d.:-]+)</b></td>' + validuntil = re.search(validuntil, page, re.M) + validuntil = validuntil.group(1).strip() + validuntil = mktime(strptime(validuntil, "%d.%m.%Y - %H:%M")) + + traffic = r'Traffic verbleibend:</td>\s*<td>([^<]+)' + traffic = re.search(traffic, page, re.M) + traffic = traffic.group(1).strip() + traffic = self.parseTraffic(traffic) + + return {"validuntil": validuntil, "trafficleft": traffic} + + + def login(self, user, data, req): + req.load("http://freakshare.com/index.php?language=EN") + + page = req.load("http://freakshare.com/login.html", None, + {"submit": "Login", "user": user, "pass": data['password']}, cookies=True) + + if ">Wrong Username or Password" in page: + self.wrongPassword() diff --git a/module/plugins/accounts/FreeWayMe.py b/module/plugins/accounts/FreeWayMe.py new file mode 100644 index 000000000..7b57cc6a0 --- /dev/null +++ b/module/plugins/accounts/FreeWayMe.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class FreeWayMe(Account): + __name__ = "FreeWayMe" + __type__ = "account" + __version__ = "0.11" + + __description__ = """FreeWayMe account plugin""" + __license__ = "GPLv3" + __authors__ = [("Nicolas Giese", "james@free-way.me")] + + + def loadAccountInfo(self, user, req): + status = self.getAccountStatus(user, req) + if not status: + return False + self.logDebug(status) + + account_info = {"validuntil": -1, "premium": False} + if status['premium'] == "Free": + account_info['trafficleft'] = int(status['guthaben']) * 1024 + elif status['premium'] == "Spender": + account_info['trafficleft'] = -1 + elif status['premium'] == "Flatrate": + account_info = {"validuntil": int(status['Flatrate']), + "trafficleft": -1, + "premium": True} + + return account_info + + + def getpw(self, user): + return self.accounts[user]['password'] + + + def login(self, user, data, req): + status = self.getAccountStatus(user, req) + + # Check if user and password are valid + if not status: + self.wrongPassword() + + + def getAccountStatus(self, user, req): + answer = req.load("https://www.free-way.me/ajax/jd.php", + get={"id": 4, "user": user, "pass": self.accounts[user]['password']}) + self.logDebug("Login: %s" % answer) + if answer == "Invalid login": + self.wrongPassword() + return False + return json_loads(answer) diff --git a/module/plugins/accounts/FshareVn.py b/module/plugins/accounts/FshareVn.py new file mode 100644 index 000000000..c84ce6648 --- /dev/null +++ b/module/plugins/accounts/FshareVn.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +from time import mktime, strptime +from pycurl import REFERER +import re + +from pyload.plugin.Account import Account + + +class FshareVn(Account): + __name__ = "FshareVn" + __type__ = "account" + __version__ = "0.07" + + __description__ = """Fshare.vn account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), + ("stickell", "l.stickell@yahoo.it")] + + + VALID_UNTIL_PATTERN = ur'<dt>Thời hạn dùng:</dt>\s*<dd>([^<]+)</dd>' + LIFETIME_PATTERN = ur'<dt>Lần đăng nhập trước:</dt>\s*<dd>[^<]+</dd>' + TRAFFIC_LEFT_PATTERN = ur'<dt>Tổng Dung Lượng Tài Khoản</dt>\s*<dd[^>]*>([\d.]+) ([kKMG])B</dd>' + DIRECT_DOWNLOAD_PATTERN = ur'<input type="checkbox"\s*([^=>]*)[^>]*/>Kích hoạt download trực tiếp</dt>' + + + def loadAccountInfo(self, user, req): + html = req.load("http://www.fshare.vn/account_info.php", decode=True) + + if re.search(self.LIFETIME_PATTERN, html): + self.logDebug("Lifetime membership detected") + trafficleft = self.getTrafficLeft() + return {"validuntil": -1, "trafficleft": trafficleft, "premium": True} + + m = re.search(self.VALID_UNTIL_PATTERN, html) + if m: + premium = True + validuntil = mktime(strptime(m.group(1), '%I:%M:%S %p %d-%m-%Y')) + trafficleft = self.getTrafficLeft() + else: + premium = False + validuntil = None + trafficleft = None + + return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium} + + + def login(self, user, data, req): + req.http.c.setopt(REFERER, "https://www.fshare.vn/login.php") + + html = req.load('https://www.fshare.vn/login.php', post={ + "login_password": data['password'], + "login_useremail": user, + "url_refe": "http://www.fshare.vn/index.php" + }, referer=True, decode=True) + + if not re.search(r'<img\s+alt="VIP"', html): + self.wrongPassword() + + + def getTrafficLeft(self): + m = re.search(self.TRAFFIC_LEFT_PATTERN, html) + return float(m.group(1)) * 1024 ** {'k': 0, 'K': 0, 'M': 1, 'G': 2}[m.group(2)] if m else 0 diff --git a/module/plugins/accounts/Ftp.py b/module/plugins/accounts/Ftp.py new file mode 100644 index 000000000..67cde2cdd --- /dev/null +++ b/module/plugins/accounts/Ftp.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + + +class Ftp(Account): + __name__ = "Ftp" + __type__ = "account" + __version__ = "0.01" + + __description__ = """Ftp dummy account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + login_timeout = -1 #: Unlimited + info_threshold = -1 #: Unlimited diff --git a/module/plugins/accounts/HellshareCz.py b/module/plugins/accounts/HellshareCz.py new file mode 100644 index 000000000..6f160fa11 --- /dev/null +++ b/module/plugins/accounts/HellshareCz.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class HellshareCz(Account): + __name__ = "HellshareCz" + __type__ = "account" + __version__ = "0.14" + + __description__ = """Hellshare.cz account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + CREDIT_LEFT_PATTERN = r'<div class="credit-link">\s*<table>\s*<tr>\s*<th>(\d+|\d\d\.\d\d\.)</th>' + + + def loadAccountInfo(self, user, req): + self.relogin(user) + html = req.load("http://www.hellshare.com/") + + m = re.search(self.CREDIT_LEFT_PATTERN, html) + if m is None: + trafficleft = None + validuntil = None + premium = False + else: + credit = m.group(1) + premium = True + try: + if "." in credit: + #Time-based account + vt = [int(x) for x in credit.split('.')[:2]] + lt = time.localtime() + year = lt.tm_year + int(vt[1] < lt.tm_mon or (vt[1] == lt.tm_mon and vt[0] < lt.tm_mday)) + validuntil = time.mktime(time.strptime("%s%d 23:59:59" % (credit, year), "%d.%m.%Y %H:%M:%S")) + trafficleft = -1 + else: + #Traffic-based account + trafficleft = int(credit) * 1024 + validuntil = -1 + except Exception, e: + self.logError(_("Unable to parse credit info"), e) + validuntil = -1 + trafficleft = -1 + + return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium} + + + def login(self, user, data, req): + html = req.load('http://www.hellshare.com/') + if req.lastEffectiveURL != 'http://www.hellshare.com/': + #Switch to English + self.logDebug("Switch lang - URL: %s" % req.lastEffectiveURL) + json = req.load("%s?do=locRouter-show" % req.lastEffectiveURL) + hash = re.search(r"(\-\-[0-9a-f]+\-)", json).group(1) + self.logDebug("Switch lang - HASH: %s" % hash) + html = req.load('http://www.hellshare.com/%s/' % hash) + + if re.search(self.CREDIT_LEFT_PATTERN, html): + self.logDebug("Already logged in") + return + + html = req.load('http://www.hellshare.com/login?do=loginForm-submit', post={ + "login": "Log in", + "password": data['password'], + "username": user, + "perm_login": "on" + }) + + if "<p>You input a wrong user name or wrong password</p>" in html: + self.wrongPassword() diff --git a/module/plugins/accounts/Http.py b/module/plugins/accounts/Http.py new file mode 100644 index 000000000..2571ef712 --- /dev/null +++ b/module/plugins/accounts/Http.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + + +class Http(Account): + __name__ = "Http" + __type__ = "account" + __version__ = "0.01" + + __description__ = """Http dummy account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + login_timeout = -1 #: Unlimited + info_threshold = -1 #: Unlimited diff --git a/module/plugins/accounts/HugefilesNet.py b/module/plugins/accounts/HugefilesNet.py new file mode 100644 index 000000000..eb383fb17 --- /dev/null +++ b/module/plugins/accounts/HugefilesNet.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class HugefilesNet(XFSAccount): + __name__ = "HugefilesNet" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Hugefiles.net account plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + HOSTER_DOMAIN = "hugefiles.net" diff --git a/module/plugins/accounts/HundredEightyUploadCom.py b/module/plugins/accounts/HundredEightyUploadCom.py new file mode 100644 index 000000000..79b5ee033 --- /dev/null +++ b/module/plugins/accounts/HundredEightyUploadCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class HundredEightyUploadCom(XFSAccount): + __name__ = "HundredEightyUploadCom" + __type__ = "account" + __version__ = "0.02" + + __description__ = """180upload.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + HOSTER_DOMAIN = "180upload.com" diff --git a/module/plugins/accounts/JunocloudMe.py b/module/plugins/accounts/JunocloudMe.py new file mode 100644 index 000000000..0ffa92eb6 --- /dev/null +++ b/module/plugins/accounts/JunocloudMe.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class JunocloudMe(XFSAccount): + __name__ = "JunocloudMe" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Junocloud.me account plugin""" + __license__ = "GPLv3" + __authors__ = [("guidobelix", "guidobelix@hotmail.it")] + + + HOSTER_DOMAIN = "junocloud.me" diff --git a/module/plugins/accounts/Keep2ShareCc.py b/module/plugins/accounts/Keep2ShareCc.py new file mode 100644 index 000000000..7ed15dc62 --- /dev/null +++ b/module/plugins/accounts/Keep2ShareCc.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- + +import re + +from time import gmtime, mktime, strptime + +from pyload.plugin.Account import Account + + +class Keep2shareCc(Account): + __name__ = "Keep2shareCc" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Keep2share.cc account plugin""" + __license__ = "GPLv3" + __authors__ = [("aeronaut", "aeronaut@pianoguy.de")] + + + VALID_UNTIL_PATTERN = r'Premium expires: <b>(.+?)</b>' + TRAFFIC_LEFT_PATTERN = r'Available traffic \(today\):<b><a href="/user/statistic.html">(.+?)</a>' + + LOGIN_FAIL_PATTERN = r'Please fix the following input errors' + + + def loadAccountInfo(self, user, req): + validuntil = None + trafficleft = None + premium = None + + html = req.load("http://keep2share.cc/site/profile.html", decode=True) + + m = re.search(self.VALID_UNTIL_PATTERN, html) + if m: + expiredate = m.group(1).strip() + self.logDebug("Expire date: " + expiredate) + + try: + validuntil = mktime(strptime(expiredate, "%Y.%m.%d")) + + except Exception, e: + self.logError(e) + + else: + if validuntil > mktime(gmtime()): + premium = True + else: + premium = False + validuntil = None + + m = re.search(self.TRAFFIC_LEFT_PATTERN, html) + if m: + try: + trafficleft = self.parseTraffic(m.group(1)) + + except Exception, e: + self.logError(e) + + return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium} + + + def login(self, user, data, req): + req.cj.setCookie("keep2share.cc", "lang", "en") + + html = req.load("http://keep2share.cc/login.html", + post={'LoginForm[username]': user, 'LoginForm[password]': data['password']}) + + if re.search(self.LOGIN_FAIL_PATTERN, html): + self.wrongPassword() diff --git a/module/plugins/accounts/LetitbitNet.py b/module/plugins/accounts/LetitbitNet.py new file mode 100644 index 000000000..c7615dd56 --- /dev/null +++ b/module/plugins/accounts/LetitbitNet.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +# from pyload.utils import json_loads, json_dumps + + +class LetitbitNet(Account): + __name__ = "LetitbitNet" + __type__ = "account" + __version__ = "0.01" + + __description__ = """Letitbit.net account plugin""" + __license__ = "GPLv3" + __authors__ = [("stickell", "l.stickell@yahoo.it")] + + + def loadAccountInfo(self, user, req): + ## DISABLED BECAUSE IT GET 'key exausted' EVEN IF VALID ## + # api_key = self.accounts[user]['password'] + # json_data = [api_key, ['key/info']] + # api_rep = req.load('http://api.letitbit.net/json', post={'r': json_dumps(json_data)}) + # self.logDebug("API Key Info: " + api_rep) + # api_rep = json_loads(api_rep) + # + # if api_rep['status'] == 'FAIL': + # self.logWarning(api_rep['data']) + # return {'valid': False, 'premium': False} + + return {"premium": True} + + + def login(self, user, data, req): + # API_KEY is the username and the PREMIUM_KEY is the password + self.logInfo(_("You must use your API KEY as username and the PREMIUM KEY as password")) diff --git a/module/plugins/accounts/LinestorageCom.py b/module/plugins/accounts/LinestorageCom.py new file mode 100644 index 000000000..6a9991b17 --- /dev/null +++ b/module/plugins/accounts/LinestorageCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class LinestorageCom(XFSAccount): + __name__ = "LinestorageCom" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Linestorage.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + HOSTER_DOMAIN = "linestorage.com" diff --git a/module/plugins/accounts/LinksnappyCom.py b/module/plugins/accounts/LinksnappyCom.py new file mode 100644 index 000000000..968271dd7 --- /dev/null +++ b/module/plugins/accounts/LinksnappyCom.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- + +from hashlib import md5 + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class LinksnappyCom(Account): + __name__ = "LinksnappyCom" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Linksnappy.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("stickell", "l.stickell@yahoo.it")] + + + def loadAccountInfo(self, user, req): + data = self.getAccountData(user) + r = req.load('http://gen.linksnappy.com/lseAPI.php', + get={'act': 'USERDETAILS', 'username': user, 'password': md5(data['password']).hexdigest()}) + self.logDebug("JSON data: " + r) + j = json_loads(r) + + if j['error']: + return {"premium": False} + + validuntil = j['return']['expire'] + if validuntil == 'lifetime': + validuntil = -1 + elif validuntil == 'expired': + return {"premium": False} + else: + validuntil = float(validuntil) + + if 'trafficleft' not in j['return'] or isinstance(j['return']['trafficleft'], str): + trafficleft = -1 + else: + trafficleft = int(j['return']['trafficleft']) * 1024 + + return {"premium": True, "validuntil": validuntil, "trafficleft": trafficleft} + + + def login(self, user, data, req): + r = req.load('http://gen.linksnappy.com/lseAPI.php', + get={'act': 'USERDETAILS', 'username': user, 'password': md5(data['password']).hexdigest()}) + + if 'Invalid Account Details' in r: + self.wrongPassword() diff --git a/module/plugins/accounts/LomafileCom.py b/module/plugins/accounts/LomafileCom.py new file mode 100644 index 000000000..24f988ac1 --- /dev/null +++ b/module/plugins/accounts/LomafileCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class LomafileCom(XFSAccount): + __name__ = "LomafileCom" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Lomafile.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("guidobelix", "guidobelix@hotmail.it")] + + + HOSTER_DOMAIN = "lomafile.com" diff --git a/module/plugins/accounts/MegaDebridEu.py b/module/plugins/accounts/MegaDebridEu.py new file mode 100644 index 000000000..c2e64bcc7 --- /dev/null +++ b/module/plugins/accounts/MegaDebridEu.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class MegaDebridEu(Account): + __name__ = "MegaDebridEu" + __type__ = "account" + __version__ = "0.20" + + __description__ = """mega-debrid.eu account plugin""" + __license__ = "GPLv3" + __authors__ = [("D.Ducatel", "dducatel@je-geek.fr")] + + + # Define the base URL of MegaDebrid api + API_URL = "https://www.mega-debrid.eu/api.php" + + + def loadAccountInfo(self, user, req): + data = self.getAccountData(user) + jsonResponse = req.load(self.API_URL, + get={'action': 'connectUser', 'login': user, 'password': data['password']}) + res = json_loads(jsonResponse) + + if res['response_code'] == "ok": + return {"premium": True, "validuntil": float(res['vip_end']), "status": True} + else: + self.logError(res) + return {"status": False, "premium": False} + + + def login(self, user, data, req): + jsonResponse = req.load(self.API_URL, + get={'action': 'connectUser', 'login': user, 'password': data['password']}) + res = json_loads(jsonResponse) + if res['response_code'] != "ok": + self.wrongPassword() diff --git a/module/plugins/accounts/MegaRapidCz.py b/module/plugins/accounts/MegaRapidCz.py new file mode 100644 index 000000000..5ceee1301 --- /dev/null +++ b/module/plugins/accounts/MegaRapidCz.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +import re + +from time import mktime, strptime +from pyload.plugin.Account import Account + + +class MegaRapidCz(Account): + __name__ = "MegaRapidCz" + __type__ = "account" + __version__ = "0.34" + + __description__ = """MegaRapid.cz account plugin""" + __license__ = "GPLv3" + __authors__ = [("MikyWoW", "mikywow@seznam.cz"), + ("zoidberg", "zoidberg@mujmail.cz")] + + + login_timeout = 60 + + LIMITDL_PATTERN = ur'<td>Max. počet paralelních stahování: </td><td>(\d+)' + VALID_UNTIL_PATTERN = ur'<td>Paušální stahování aktivní. Vyprší </td><td><strong>(.*?)</strong>' + TRAFFIC_LEFT_PATTERN = r'<tr><td>Kredit</td><td>(.*?) GiB' + + + def loadAccountInfo(self, user, req): + html = req.load("http://megarapid.cz/mujucet/", decode=True) + + m = re.search(self.LIMITDL_PATTERN, html) + if m: + data = self.getAccountData(user) + data['options']['limitDL'] = [int(m.group(1))] + + m = re.search(self.VALID_UNTIL_PATTERN, html) + if m: + validuntil = mktime(strptime(m.group(1), "%d.%m.%Y - %H:%M")) + return {"premium": True, "trafficleft": -1, "validuntil": validuntil} + + m = re.search(self.TRAFFIC_LEFT_PATTERN, html) + if m: + trafficleft = float(m.group(1)) * (1 << 20) + return {"premium": True, "trafficleft": trafficleft, "validuntil": -1} + + return {"premium": False, "trafficleft": None, "validuntil": None} + + + def login(self, user, data, req): + htm = req.load("http://megarapid.cz/prihlaseni/") + if "Heslo:" in htm: + start = htm.index('id="inp_hash" name="hash" value="') + htm = htm[start + 33:] + hashes = htm[0:32] + htm = req.load("http://megarapid.cz/prihlaseni/", + post={"hash": hashes, + "login": user, + "pass1": data['password'], + "remember": 0, + "sbmt": u"Přihlásit"}) diff --git a/module/plugins/accounts/MegasharesCom.py b/module/plugins/accounts/MegasharesCom.py new file mode 100644 index 000000000..352e0abd5 --- /dev/null +++ b/module/plugins/accounts/MegasharesCom.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +import re +from time import mktime, strptime + +from pyload.plugin.Account import Account + + +class MegasharesCom(Account): + __name__ = "MegasharesCom" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Megashares.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + VALID_UNTIL_PATTERN = r'<p class="premium_info_box">Period Ends: (\w{3} \d{1,2}, \d{4})</p>' + + + def loadAccountInfo(self, user, req): + #self.relogin(user) + html = req.load("http://d01.megashares.com/myms.php", decode=True) + + premium = False if '>Premium Upgrade<' in html else True + + validuntil = trafficleft = -1 + try: + timestr = re.search(self.VALID_UNTIL_PATTERN, html).group(1) + self.logDebug(timestr) + validuntil = mktime(strptime(timestr, "%b %d, %Y")) + except Exception, e: + self.logError(e) + + return {"validuntil": validuntil, "trafficleft": -1, "premium": premium} + + + def login(self, user, data, req): + html = req.load('http://d01.megashares.com/myms_login.php', post={ + "httpref": "", + "myms_login": "Login", + "mymslogin_name": user, + "mymspassword": data['password'] + }, decode=True) + + if not '<span class="b ml">%s</span>' % user in html: + self.wrongPassword() diff --git a/module/plugins/accounts/MovReelCom.py b/module/plugins/accounts/MovReelCom.py new file mode 100644 index 000000000..4d2855de1 --- /dev/null +++ b/module/plugins/accounts/MovReelCom.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class MovReelCom(XFSAccount): + __name__ = "MovReelCom" + __type__ = "account" + __version__ = "0.03" + + __description__ = """Movreel.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")] + + + login_timeout = 60 + info_threshold = 30 + + HOSTER_DOMAIN = "movreel.com" diff --git a/module/plugins/accounts/MultishareCz.py b/module/plugins/accounts/MultishareCz.py new file mode 100644 index 000000000..62b48c546 --- /dev/null +++ b/module/plugins/accounts/MultishareCz.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Account import Account + + +class MultishareCz(Account): + __name__ = "MultishareCz" + __type__ = "account" + __version__ = "0.03" + + __description__ = """Multishare.cz account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + TRAFFIC_LEFT_PATTERN = r'<span class="profil-zvyrazneni">Kredit:</span>\s*<strong>(?P<S>[\d.,]+) (?P<U>[\w^_]+)</strong>' + ACCOUNT_INFO_PATTERN = r'<input type="hidden" id="(u_ID|u_hash)" name="[^"]*" value="([^"]+)">' + + + def loadAccountInfo(self, user, req): + #self.relogin(user) + html = req.load("http://www.multishare.cz/profil/", decode=True) + + m = re.search(self.TRAFFIC_LEFT_PATTERN, html) + trafficleft = self.parseTraffic(m.group('S'), m.group('U')) if m else 0 + self.premium = True if trafficleft else False + + html = req.load("http://www.multishare.cz/", decode=True) + mms_info = dict(re.findall(self.ACCOUNT_INFO_PATTERN, html)) + + return dict(mms_info, **{"validuntil": -1, "trafficleft": trafficleft}) + + + def login(self, user, data, req): + html = req.load('http://www.multishare.cz/html/prihlaseni_process.php', post={ + "akce": "Přihlásit", + "heslo": data['password'], + "jmeno": user + }, decode=True) + + if '<div class="akce-chyba akce">' in html: + self.wrongPassword() diff --git a/module/plugins/accounts/MyfastfileCom.py b/module/plugins/accounts/MyfastfileCom.py new file mode 100644 index 000000000..01caf5c69 --- /dev/null +++ b/module/plugins/accounts/MyfastfileCom.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +from time import time + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class MyfastfileCom(Account): + __name__ = "MyfastfileCom" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Myfastfile.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("stickell", "l.stickell@yahoo.it")] + + + def loadAccountInfo(self, user, req): + if 'days_left' in self.json_data: + validuntil = int(time() + self.json_data['days_left'] * 24 * 60 * 60) + return {"premium": True, "validuntil": validuntil, "trafficleft": -1} + else: + self.logError(_("Unable to get account information")) + + + def login(self, user, data, req): + # Password to use is the API-Password written in http://myfastfile.com/myaccount + html = req.load("http://myfastfile.com/api.php", + get={"user": user, "pass": data['password']}) + self.logDebug("JSON data: " + html) + self.json_data = json_loads(html) + if self.json_data['status'] != 'ok': + self.logError(_('Invalid login. The password to use is the API-Password you find in your "My Account" page')) + self.wrongPassword() diff --git a/module/plugins/accounts/NetloadIn.py b/module/plugins/accounts/NetloadIn.py new file mode 100644 index 000000000..d0d48315a --- /dev/null +++ b/module/plugins/accounts/NetloadIn.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +import re +from time import time + +from pyload.plugin.Account import Account + + +class NetloadIn(Account): + __name__ = "NetloadIn" + __type__ = "account" + __version__ = "0.22" + + __description__ = """Netload.in account plugin""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org"), + ("CryNickSystems", "webmaster@pcProfil.de")] + + + def loadAccountInfo(self, user, req): + page = req.load("http://netload.in/index.php", get={'id': 2, 'lang': "de"}) + left = r'>(\d+) (Tag|Tage), (\d+) Stunden<' + left = re.search(left, page) + if left: + validuntil = time() + int(left.group(1)) * 24 * 60 * 60 + int(left.group(3)) * 60 * 60 + trafficleft = -1 + premium = True + else: + validuntil = None + premium = False + trafficleft = None + return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium} + + + def login(self, user, data, req): + page = req.load("http://netload.in/index.php", None, + {"txtuser": user, "txtpass": data['password'], "txtcheck": "login", "txtlogin": "Login"}, + cookies=True) + if "password or it might be invalid!" in page: + self.wrongPassword() diff --git a/module/plugins/accounts/NosuploadCom.py b/module/plugins/accounts/NosuploadCom.py new file mode 100644 index 000000000..7fc8b49de --- /dev/null +++ b/module/plugins/accounts/NosuploadCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class NosuploadCom(XFSAccount): + __name__ = "NosuploadCom" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Nosupload.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + HOSTER_DOMAIN = "nosupload.com" diff --git a/module/plugins/accounts/NovafileCom.py b/module/plugins/accounts/NovafileCom.py new file mode 100644 index 000000000..71a7dc2dc --- /dev/null +++ b/module/plugins/accounts/NovafileCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class NovafileCom(XFSAccount): + __name__ = "NovafileCom" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Novafile.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + HOSTER_DOMAIN = "novafile.com" diff --git a/module/plugins/accounts/NowVideoSx.py b/module/plugins/accounts/NowVideoSx.py new file mode 100644 index 000000000..d2527d635 --- /dev/null +++ b/module/plugins/accounts/NowVideoSx.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +import re + +from time import gmtime, mktime, strptime + +from pyload.plugin.Account import Account + + +class NowVideoAt(Account): + __name__ = "NowVideoAt" + __type__ = "account" + __version__ = "0.01" + + __description__ = """NowVideo.at account plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + VALID_UNTIL_PATTERN = r'>Your premium membership expires on: (.+?)<' + + + def loadAccountInfo(self, user, req): + validuntil = None + trafficleft = -1 + premium = None + + html = req.load("http://www.nowvideo.at/premium.php") + + m = re.search(self.VALID_UNTIL_PATTERN, html) + if m: + expiredate = m.group(1).strip() + self.logDebug("Expire date: " + expiredate) + + try: + validuntil = mktime(strptime(expiredate, "%Y-%b-%d")) + + except Exception, e: + self.logError(e) + + else: + if validuntil > mktime(gmtime()): + premium = True + else: + premium = False + validuntil = -1 + + return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium} + + + def login(self, user, data, req): + html = req.load("http://www.nowvideo.at/login.php", + post={'user': user, 'pass': data['password']}) + + if ">Invalid login details" is html: + self.wrongPassword() diff --git a/module/plugins/accounts/OboomCom.py b/module/plugins/accounts/OboomCom.py new file mode 100644 index 000000000..a40783535 --- /dev/null +++ b/module/plugins/accounts/OboomCom.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- + +import time + +from beaker.crypto.pbkdf2 import PBKDF2 + +from pyload.utils import json_loads +from pyload.plugin.Account import Account + + +class OboomCom(Account): + __name__ = "OboomCom" + __type__ = "account" + __version__ = "0.21" + + __description__ = """Oboom.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("stanley", "stanley.foerster@gmail.com")] + + + def loadAccountData(self, user, req): + passwd = self.getAccountData(user)['password'] + salt = passwd[::-1] + pbkdf2 = PBKDF2(passwd, salt, 1000).hexread(16) + result = json_loads(req.load("https://www.oboom.com/1/login", get={"auth": user, "pass": pbkdf2})) + if not result[0] == 200: + self.logWarning(_("Failed to log in: %s") % result[1]) + self.wrongPassword() + return result[1] + + + def loadAccountInfo(self, name, req): + accountData = self.loadAccountData(name, req) + + userData = accountData['user'] + + if userData['premium'] == "null": + premium = False + else: + premium = True + + if userData['premium_unix'] == "null": + validUntil = -1 + else: + validUntil = int(userData['premium_unix']) + + traffic = userData['traffic'] + + trafficLeft = traffic['current'] + maxTraffic = traffic['max'] + + session = accountData['session'] + + return {'premium' : premium, + 'validuntil' : validUntil, + 'trafficleft': trafficLeft / 1024, #@TODO: Remove / 1024 in 0.4.10 + 'maxtraffic' : maxTraffic / 1024, #@TODO: Remove / 1024 in 0.4.10 + 'session' : session} + + + def login(self, user, data, req): + self.loadAccountData(user, req) diff --git a/module/plugins/accounts/OneFichierCom.py b/module/plugins/accounts/OneFichierCom.py new file mode 100644 index 000000000..5d4a037f0 --- /dev/null +++ b/module/plugins/accounts/OneFichierCom.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +import re + +from time import strptime, mktime + +from pycurl import REFERER + +from pyload.plugin.Account import Account + + +class OneFichierCom(Account): + __name__ = "OneFichierCom" + __type__ = "account" + __version__ = "0.11" + + __description__ = """1fichier.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("Elrick69", "elrick69[AT]rocketmail[DOT]com"), + ("Walter Purcaro", "vuolter@gmail.com")] + + + VALID_UNTIL_PATTERN = r'Your Premium Status will end the (\d+/\d+/\d+)' + + + def loadAccountInfo(self, user, req): + validuntil = None + trafficleft = -1 + premium = None + + html = req.load("https://1fichier.com/console/abo.pl") + + m = re.search(self.VALID_UNTIL_PATTERN, html) + if m: + expiredate = m.group(1) + self.logDebug("Expire date: " + expiredate) + + try: + validuntil = mktime(strptime(expiredate, "%d/%m/%Y")) + except Exception, e: + self.logError(e) + else: + premium = True + + return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium or False} + + + def login(self, user, data, req): + req.http.c.setopt(REFERER, "https://1fichier.com/login.pl?lg=en") + + html = req.load("https://1fichier.com/login.pl?lg=en", + post={'mail': user, 'pass': data['password'], 'It': "on", 'purge': "off", 'valider': "Send"}) + + if '>Invalid email address' in html or '>Invalid password' in html: + self.wrongPassword() diff --git a/module/plugins/accounts/OverLoadMe.py b/module/plugins/accounts/OverLoadMe.py new file mode 100644 index 000000000..27ed9eb98 --- /dev/null +++ b/module/plugins/accounts/OverLoadMe.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class OverLoadMe(Account): + __name__ = "OverLoadMe" + __type__ = "account" + __version__ = "0.01" + + __description__ = """Over-Load.me account plugin""" + __license__ = "GPLv3" + __authors__ = [("marley", "marley@over-load.me")] + + + def loadAccountInfo(self, user, req): + data = self.getAccountData(user) + page = req.load("https://api.over-load.me/account.php", get={"user": user, "auth": data['password']}).strip() + data = json_loads(page) + + # Check for premium + if data['membership'] == "Free": + return {"premium": False} + + account_info = {"validuntil": data['expirationunix'], "trafficleft": -1} + return account_info + + + def login(self, user, data, req): + jsondata = req.load("https://api.over-load.me/account.php", + get={"user": user, "auth": data['password']}).strip() + data = json_loads(jsondata) + + if data['err'] == 1: + self.wrongPassword() diff --git a/module/plugins/accounts/PremiumTo.py b/module/plugins/accounts/PremiumTo.py new file mode 100644 index 000000000..7f15e8a86 --- /dev/null +++ b/module/plugins/accounts/PremiumTo.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + + +class PremiumTo(Account): + __name__ = "PremiumTo" + __type__ = "account" + __version__ = "0.04" + + __description__ = """Premium.to account plugin""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org"), + ("zoidberg", "zoidberg@mujmail.cz"), + ("stickell", "l.stickell@yahoo.it")] + + + + def loadAccountInfo(self, user, req): + api_r = req.load("http://premium.to/api/straffic.php", + get={'username': self.username, 'password': self.password}) + traffic = sum(map(int, api_r.split(';'))) + + return {"trafficleft": int(traffic) / 1024, "validuntil": -1} #@TODO: Remove / 1024 in 0.4.10 + + + def login(self, user, data, req): + self.username = user + self.password = data['password'] + authcode = req.load("http://premium.to/api/getauthcode.php", + get={'username': user, 'password': self.password}).strip() + + if "wrong username" in authcode: + self.wrongPassword() diff --git a/module/plugins/accounts/PremiumizeMe.py b/module/plugins/accounts/PremiumizeMe.py new file mode 100644 index 000000000..75fc22198 --- /dev/null +++ b/module/plugins/accounts/PremiumizeMe.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + +from pyload.utils import json_loads + + +class PremiumizeMe(Account): + __name__ = "PremiumizeMe" + __type__ = "account" + __version__ = "0.11" + + __description__ = """Premiumize.me account plugin""" + __license__ = "GPLv3" + __authors__ = [("Florian Franzen", "FlorianFranzen@gmail.com")] + + + def loadAccountInfo(self, user, req): + # Get user data from premiumize.me + status = self.getAccountStatus(user, req) + self.logDebug(status) + + # Parse account info + account_info = {"validuntil": float(status['result']['expires']), + "trafficleft": max(0, status['result']['trafficleft_bytes'])} + + if status['result']['type'] == 'free': + account_info['premium'] = False + + return account_info + + + def login(self, user, data, req): + # Get user data from premiumize.me + status = self.getAccountStatus(user, req) + + # Check if user and password are valid + if status['status'] != 200: + self.wrongPassword() + + + def getAccountStatus(self, user, req): + # Use premiumize.me API v1 (see https://secure.premiumize.me/?show=api) + # to retrieve account info and return the parsed json answer + answer = req.load("https://api.premiumize.me/pm-api/v1.php", + get={'method' : "accountstatus", + 'params[login]': user, + 'params[pass]' : self.accounts[user]['password']}) + return json_loads(answer) diff --git a/module/plugins/accounts/QuickshareCz.py b/module/plugins/accounts/QuickshareCz.py new file mode 100644 index 000000000..c3d69e0c3 --- /dev/null +++ b/module/plugins/accounts/QuickshareCz.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Account import Account + + +class QuickshareCz(Account): + __name__ = "QuickshareCz" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Quickshare.cz account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + TRAFFIC_LEFT_PATTERN = r'Stav kreditu: <strong>(.+?)</strong>' + + + def loadAccountInfo(self, user, req): + html = req.load("http://www.quickshare.cz/premium", decode=True) + + m = re.search(self.TRAFFIC_LEFT_PATTERN, html) + if m: + trafficleft = self.parseTraffic(m.group(1)) + premium = True if trafficleft else False + else: + trafficleft = None + premium = False + + return {"validuntil": -1, "trafficleft": trafficleft, "premium": premium} + + + def login(self, user, data, req): + html = req.load('http://www.quickshare.cz/html/prihlaseni_process.php', post={ + "akce": u'Přihlásit', + "heslo": data['password'], + "jmeno": user + }, decode=True) + + if u'>Takový uživatel neexistuje.<' in html or u'>Špatné heslo.<' in html: + self.wrongPassword() diff --git a/module/plugins/accounts/RPNetBiz.py b/module/plugins/accounts/RPNetBiz.py new file mode 100644 index 000000000..bb529b19c --- /dev/null +++ b/module/plugins/accounts/RPNetBiz.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class RPNetBiz(Account): + __name__ = "RPNetBiz" + __type__ = "account" + __version__ = "0.10" + + __description__ = """RPNet.biz account plugin""" + __license__ = "GPLv3" + __authors__ = [("Dman", "dmanugm@gmail.com")] + + + def loadAccountInfo(self, user, req): + # Get account information from rpnet.biz + res = self.getAccountStatus(user, req) + try: + if res['accountInfo']['isPremium']: + # Parse account info. Change the trafficleft later to support per host info. + account_info = {"validuntil": int(res['accountInfo']['premiumExpiry']), + "trafficleft": -1, "premium": True} + else: + account_info = {"validuntil": None, "trafficleft": None, "premium": False} + + except KeyError: + #handle wrong password exception + account_info = {"validuntil": None, "trafficleft": None, "premium": False} + + return account_info + + + def login(self, user, data, req): + # Get account information from rpnet.biz + res = self.getAccountStatus(user, req) + + # If we have an error in the res, we have wrong login information + if 'error' in res: + self.wrongPassword() + + + def getAccountStatus(self, user, req): + # Using the rpnet API, check if valid premium account + res = req.load("https://premium.rpnet.biz/client_api.php", + get={"username": user, "password": self.accounts[user]['password'], + "action": "showAccountInformation"}) + self.logDebug("JSON data: %s" % res) + + return json_loads(res) diff --git a/module/plugins/accounts/RapidfileshareNet.py b/module/plugins/accounts/RapidfileshareNet.py new file mode 100644 index 000000000..ec0bf8db4 --- /dev/null +++ b/module/plugins/accounts/RapidfileshareNet.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class RapidfileshareNet(XFSAccount): + __name__ = "RapidfileshareNet" + __type__ = "account" + __version__ = "0.05" + + __description__ = """Rapidfileshare.net account plugin""" + __license__ = "GPLv3" + __authors__ = [("guidobelix", "guidobelix@hotmail.it")] + + + HOSTER_DOMAIN = "rapidfileshare.net" + + TRAFFIC_LEFT_PATTERN = r'>Traffic available today:</TD><TD><label for="name">\s*(?P<S>[\d.,]+)\s*(?:(?P<U>[\w^_]+))?' diff --git a/module/plugins/accounts/RapidgatorNet.py b/module/plugins/accounts/RapidgatorNet.py new file mode 100644 index 000000000..693b78547 --- /dev/null +++ b/module/plugins/accounts/RapidgatorNet.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class RapidgatorNet(Account): + __name__ = "RapidgatorNet" + __type__ = "account" + __version__ = "0.04" + + __description__ = """Rapidgator.net account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + API_URL = 'http://rapidgator.net/api/user' + + + def loadAccountInfo(self, user, req): + try: + sid = self.getAccountData(user).get('SID') + assert sid + + json = req.load("%s/info?sid=%s" % (self.API_URL, sid)) + self.logDebug("API:USERINFO", json) + json = json_loads(json) + + if json['response_status'] == 200: + if "reset_in" in json['response']: + self.scheduleRefresh(user, json['response']['reset_in']) + + return {"validuntil": json['response']['expire_date'], + "trafficleft": int(json['response']['traffic_left']), + "premium": True} + else: + self.logError(json['response_details']) + except Exception, e: + self.logError(e) + + return {"validuntil": None, "trafficleft": None, "premium": False} + + + def login(self, user, data, req): + try: + json = req.load('%s/login' % self.API_URL, post={"username": user, "password": data['password']}) + self.logDebug("API:LOGIN", json) + json = json_loads(json) + + if json['response_status'] == 200: + data['SID'] = str(json['response']['session_id']) + return + else: + self.logError(json['response_details']) + except Exception, e: + self.logError(e) + + self.wrongPassword() diff --git a/module/plugins/accounts/RapiduNet.py b/module/plugins/accounts/RapiduNet.py new file mode 100644 index 000000000..2c6fe04f8 --- /dev/null +++ b/module/plugins/accounts/RapiduNet.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class RapiduNet(Account): + __name__ = "RapiduNet" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Rapidu.net account plugin""" + __license__ = "GPLv3" + __authors__ = [("prOq", "")] + + + PREMIUM_PATTERN = r'<a href="premium/" style="padding-left: 0px;">Account: <b>Premium</b></a>' + + + def loadAccountInfo(self, user, req): + info = {'validuntil': None, 'trafficleft': None, 'premium': False} + + req.load("https://rapidu.net/ajax.php", get={'a': "getChangeLang"}, post={"_go": "", "lang": "en"}) + html = req.load("https://rapidu.net/", decode=True) + + if re.search(self.PREMIUM_PATTERN, html): + info['premium'] = True + + return info + + + def login(self, user, data, req): + try: + json = json_loads(req.load("https://rapidu.net/ajax.php?a=getUserLogin", + post={'_go': "", + 'login': user, + 'pass': data['password'], + 'member': "1"})) + + self.logDebug(json) + + if not json['message'] == "success": + self.wrongPassword() + + except Exception, e: + self.logError(e) diff --git a/module/plugins/accounts/RarefileNet.py b/module/plugins/accounts/RarefileNet.py new file mode 100644 index 000000000..1dc93681c --- /dev/null +++ b/module/plugins/accounts/RarefileNet.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class RarefileNet(XFSAccount): + __name__ = "RarefileNet" + __type__ = "account" + __version__ = "0.04" + + __description__ = """RareFile.net account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + HOSTER_DOMAIN = "rarefile.net" diff --git a/module/plugins/accounts/RealdebridCom.py b/module/plugins/accounts/RealdebridCom.py new file mode 100644 index 000000000..d5aa2163c --- /dev/null +++ b/module/plugins/accounts/RealdebridCom.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +import xml.dom.minidom as dom + +from pyload.plugin.Account import Account + + +class RealdebridCom(Account): + __name__ = "RealdebridCom" + __type__ = "account" + __version__ = "0.43" + + __description__ = """Real-Debrid.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("Devirex Hazzard", "naibaf_11@yahoo.de")] + + + def loadAccountInfo(self, user, req): + if self.pin_code: + return {"premium": False} + page = req.load("https://real-debrid.com/api/account.php") + xml = dom.parseString(page) + account_info = {"validuntil": int(xml.getElementsByTagName("expiration")[0].childNodes[0].nodeValue), + "trafficleft": -1} + + return account_info + + + def login(self, user, data, req): + self.pin_code = False + page = req.load("https://real-debrid.com/ajax/login.php", get={"user": user, "pass": data['password']}) + if "Your login informations are incorrect" in page: + self.wrongPassword() + elif "PIN Code required" in page: + self.logWarning(_("PIN code required. Please login to https://real-debrid.com using the PIN or disable the double authentication in your control panel on https://real-debrid.com")) + self.pin_code = True diff --git a/module/plugins/accounts/RehostTo.py b/module/plugins/accounts/RehostTo.py new file mode 100644 index 000000000..bcc34d47f --- /dev/null +++ b/module/plugins/accounts/RehostTo.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + + +class RehostTo(Account): + __name__ = "RehostTo" + __type__ = "account" + __version__ = "0.10" + + __description__ = """Rehost.to account plugin""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org")] + + + def loadAccountInfo(self, user, req): + data = self.getAccountData(user) + page = req.load("http://rehost.to/api.php", + get={'cmd': "login", 'user': user, 'pass': data['password']}) + data = [x.split("=") for x in page.split(",")] + ses = data[0][1] + long_ses = data[1][1] + + page = req.load("http://rehost.to/api.php", + get={'cmd': "get_premium_credits", 'long_ses': long_ses}) + traffic, valid = page.split(",") + + account_info = {"trafficleft": int(traffic) * 1024, + "validuntil": int(valid), + "long_ses": long_ses, + "ses": ses} + + return account_info + + + def login(self, user, data, req): + page = req.load("http://rehost.to/api.php", + get={'cmd': "login", 'user': user, 'pass': data['password']}) + + if "Login failed." in page: + self.wrongPassword() diff --git a/module/plugins/accounts/RyushareCom.py b/module/plugins/accounts/RyushareCom.py new file mode 100644 index 000000000..fcef2d634 --- /dev/null +++ b/module/plugins/accounts/RyushareCom.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class RyushareCom(XFSAccount): + __name__ = "RyushareCom" + __type__ = "account" + __version__ = "0.05" + + __description__ = """Ryushare.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), + ("trance4us", "")] + + + HOSTER_DOMAIN = "ryushare.com" + + + def login(self, user, data, req): + req.lastURL = "http://ryushare.com/login.python" + html = req.load("http://ryushare.com/login.python", + post={"login": user, "password": data['password'], "op": "login"}) + if 'Incorrect Login or Password' in html or '>Error<' in html: + self.wrongPassword() diff --git a/module/plugins/accounts/SafesharingEu.py b/module/plugins/accounts/SafesharingEu.py new file mode 100644 index 000000000..f5cbf050e --- /dev/null +++ b/module/plugins/accounts/SafesharingEu.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class SafesharingEu(XFSAccount): + __name__ = "SafesharingEu" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Safesharing.eu account plugin""" + __license__ = "GPLv3" + __authors__ = [("guidobelix", "guidobelix@hotmail.it")] + + + HOSTER_DOMAIN = "safesharing.eu" diff --git a/module/plugins/accounts/SecureUploadEu.py b/module/plugins/accounts/SecureUploadEu.py new file mode 100644 index 000000000..bb47bcba3 --- /dev/null +++ b/module/plugins/accounts/SecureUploadEu.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class SecureUploadEu(XFSAccount): + __name__ = "SecureUploadEu" + __type__ = "account" + __version__ = "0.02" + + __description__ = """SecureUpload.eu account plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + HOSTER_DOMAIN = "secureupload.eu" diff --git a/module/plugins/accounts/SendmywayCom.py b/module/plugins/accounts/SendmywayCom.py new file mode 100644 index 000000000..d64658de3 --- /dev/null +++ b/module/plugins/accounts/SendmywayCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class SendmywayCom(XFSAccount): + __name__ = "SendmywayCom" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Sendmyway.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + HOSTER_DOMAIN = "sendmyway.com" diff --git a/module/plugins/accounts/ShareonlineBiz.py b/module/plugins/accounts/ShareonlineBiz.py new file mode 100644 index 000000000..17425ac07 --- /dev/null +++ b/module/plugins/accounts/ShareonlineBiz.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + + +class ShareonlineBiz(Account): + __name__ = "ShareonlineBiz" + __type__ = "account" + __version__ = "0.24" + + __description__ = """Share-online.biz account plugin""" + __license__ = "GPLv3" + __authors__ = [("mkaay", "mkaay@mkaay.de"), + ("zoidberg", "zoidberg@mujmail.cz")] + + + def getUserAPI(self, user, req): + return req.load("http://api.share-online.biz/account.php", + {"username": user, "password": self.accounts[user]['password'], "act": "userDetails"}) + + + def loadAccountInfo(self, user, req): + html = self.getUserAPI(user, req) + + info = {} + for line in html.splitlines(): + if "=" in line: + key, value = line.split("=") + info[key] = value + self.logDebug(info) + + if "dl" in info and info['dl'].lower() != "not_available": + req.cj.setCookie("share-online.biz", "dl", info['dl']) + if "a" in info and info['a'].lower() != "not_available": + req.cj.setCookie("share-online.biz", "a", info['a']) + + return {"validuntil": int(info['expire_date']) if "expire_date" in info else -1, + "trafficleft": -1, + "premium": True if ("dl" in info or "a" in info) and (info['group'] != "Sammler") else False} + + + def login(self, user, data, req): + html = self.getUserAPI(user, req) + if "EXCEPTION" in html: + self.wrongPassword() diff --git a/module/plugins/accounts/SimplyPremiumCom.py b/module/plugins/accounts/SimplyPremiumCom.py new file mode 100644 index 000000000..0872e10f5 --- /dev/null +++ b/module/plugins/accounts/SimplyPremiumCom.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +from pyload.utils import json_loads +from pyload.plugin.Account import Account + + +class SimplyPremiumCom(Account): + __name__ = "SimplyPremiumCom" + __type__ = "account" + __version__ = "0.01" + + __description__ = """Simply-Premium.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("EvolutionClip", "evolutionclip@live.de")] + + + def loadAccountInfo(self, user, req): + json_data = req.load('http://www.simply-premium.com/api/user.php?format=json') + self.logDebug("JSON data: " + json_data) + json_data = json_loads(json_data) + + if 'vip' in json_data['result'] and json_data['result']['vip'] == 0: + return {"premium": False} + + #Time package + validuntil = float(json_data['result']['timeend']) + #Traffic package + # {"trafficleft": int(traffic), "validuntil": -1} + #trafficleft = int(json_data['result']['traffic']) + + #return {"premium": True, "validuntil": validuntil, "trafficleft": trafficleft} + return {"premium": True, "validuntil": validuntil} + + + def login(self, user, data, req): + req.cj.setCookie("simply-premium.com", "lang", "EN") + + if data['password'] == '' or data['password'] == '0': + post_data = {"key": user} + else: + post_data = {"login_name": user, "login_pass": data['password']} + + html = req.load("http://www.simply-premium.com/login.php", post=post_data) + + if 'logout' not in html: + self.wrongPassword() diff --git a/module/plugins/accounts/SimplydebridCom.py b/module/plugins/accounts/SimplydebridCom.py new file mode 100644 index 000000000..38ff90737 --- /dev/null +++ b/module/plugins/accounts/SimplydebridCom.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +from time import mktime, strptime + +from pyload.plugin.Account import Account + + +class SimplydebridCom(Account): + __name__ = "SimplydebridCom" + __type__ = "account" + __version__ = "0.10" + + __description__ = """Simply-Debrid.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("Kagenoshin", "kagenoshin@gmx.ch")] + + + def loadAccountInfo(self, user, req): + get_data = {'login': 2, 'u': self.loginname, 'p': self.password} + res = req.load("http://simply-debrid.com/api.php", get=get_data, decode=True) + data = [x.strip() for x in res.split(";")] + if str(data[0]) != "1": + return {"premium": False} + else: + return {"trafficleft": -1, "validuntil": mktime(strptime(str(data[2]), "%d/%m/%Y"))} + + + def login(self, user, data, req): + self.loginname = user + self.password = data['password'] + get_data = {'login': 1, 'u': self.loginname, 'p': self.password} + res = req.load("http://simply-debrid.com/api.php", get=get_data, decode=True) + if res != "02: loggin success": + self.wrongPassword() diff --git a/module/plugins/accounts/StahnuTo.py b/module/plugins/accounts/StahnuTo.py new file mode 100644 index 000000000..adc7ab789 --- /dev/null +++ b/module/plugins/accounts/StahnuTo.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Account import Account + + +class StahnuTo(Account): + __name__ = "StahnuTo" + __type__ = "account" + __version__ = "0.03" + + __description__ = """StahnuTo account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + def loadAccountInfo(self, user, req): + html = req.load("http://www.stahnu.to/") + + m = re.search(r'>VIP: (\d+.*)<', html) + trafficleft = self.parseTraffic(m.group(1)) * 1024 if m else 0 + + return {"premium": trafficleft > (512 * 1024), "trafficleft": trafficleft, "validuntil": -1} + + + def login(self, user, data, req): + html = req.load("http://www.stahnu.to/login.php", post={ + "username": user, + "password": data['password'], + "submit": "Login"}) + + if not '<a href="logout.php">' in html: + self.wrongPassword() diff --git a/module/plugins/accounts/StreamcloudEu.py b/module/plugins/accounts/StreamcloudEu.py new file mode 100644 index 000000000..3ac74fbd0 --- /dev/null +++ b/module/plugins/accounts/StreamcloudEu.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class StreamcloudEu(XFSAccount): + __name__ = "StreamcloudEu" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Streamcloud.eu account plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + HOSTER_DOMAIN = "streamcloud.eu" diff --git a/module/plugins/accounts/TurbobitNet.py b/module/plugins/accounts/TurbobitNet.py new file mode 100644 index 000000000..1086ede2a --- /dev/null +++ b/module/plugins/accounts/TurbobitNet.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +import re +from time import mktime, strptime + +from pyload.plugin.Account import Account + + +class TurbobitNet(Account): + __name__ = "TurbobitNet" + __type__ = "account" + __version__ = "0.01" + + __description__ = """TurbobitNet account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + def loadAccountInfo(self, user, req): + html = req.load("http://turbobit.net") + + m = re.search(r'<u>Turbo Access</u> to ([\d.]+)', html) + if m: + premium = True + validuntil = mktime(strptime(m.group(1), "%d.%m.%Y")) + else: + premium = False + validuntil = -1 + + return {"premium": premium, "trafficleft": -1, "validuntil": validuntil} + + + def login(self, user, data, req): + req.cj.setCookie("turbobit.net", "user_lang", "en") + + html = req.load("http://turbobit.net/user/login", post={ + "user[login]": user, + "user[pass]": data['password'], + "user[submit]": "Login"}) + + if not '<div class="menu-item user-name">' in html: + self.wrongPassword() diff --git a/module/plugins/accounts/TusfilesNet.py b/module/plugins/accounts/TusfilesNet.py new file mode 100644 index 000000000..84e9ef9c6 --- /dev/null +++ b/module/plugins/accounts/TusfilesNet.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +import re + +from time import mktime, strptime, gmtime + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class TusfilesNet(XFSAccount): + __name__ = "TusfilesNet" + __type__ = "account" + __version__ = "0.06" + + __description__ = """Tusfile.net account plugin""" + __license__ = "GPLv3" + __authors__ = [("guidobelix", "guidobelix@hotmail.it")] + + + HOSTER_DOMAIN = "tusfiles.net" + + VALID_UNTIL_PATTERN = r'<span class="label label-default">([^<]+)</span>' + TRAFFIC_LEFT_PATTERN = r'<td><img src="//www\.tusfiles\.net/i/icon/meter\.png" alt=""/></td>\n<td> (?P<S>[\d.,]+)' diff --git a/module/plugins/accounts/UlozTo.py b/module/plugins/accounts/UlozTo.py new file mode 100644 index 000000000..57031ce1f --- /dev/null +++ b/module/plugins/accounts/UlozTo.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from pyload.plugin.Account import Account + + +class UlozTo(Account): + __name__ = "UlozTo" + __type__ = "account" + __version__ = "0.07" + + __description__ = """Uloz.to account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), + ("pulpe", "")] + + + TRAFFIC_LEFT_PATTERN = r'<li class="menu-kredit"><a href="/kredit" title="[^"]*?GB = ([\d.]+) MB"' + + + def loadAccountInfo(self, user, req): + self.phpsessid = req.cj.getCookie("ULOSESSID") #@NOTE: this cookie gets lost somehow after each request + + html = req.load("http://www.ulozto.net/", decode=True) + + req.cj.setCookie("ulozto.net", "ULOSESSID", self.phpsessid) + + m = re.search(self.TRAFFIC_LEFT_PATTERN, html) + trafficleft = int(float(m.group(1).replace(' ', '').replace(',', '.')) * 1000 * 1.048) if m else 0 + self.premium = True if trafficleft else False + + return {"validuntil": -1, "trafficleft": trafficleft} + + + def login(self, user, data, req): + login_page = req.load('http://www.ulozto.net/?do=web-login', decode=True) + action = re.findall('<form action="(.+?)"', login_page)[1].replace('&', '&') + token = re.search('_token_" value="(.+?)"', login_page).group(1) + + html = req.load(urljoin("http://www.ulozto.net/", action), + post={'_token_' : token, + 'do' : "loginForm-submit", + 'login' : u"Přihlásit", + 'password': data['password'], + 'username': user}, + decode=True) + + if '<div class="flash error">' in html: + self.wrongPassword() diff --git a/module/plugins/accounts/UnrestrictLi.py b/module/plugins/accounts/UnrestrictLi.py new file mode 100644 index 000000000..07e7d3e17 --- /dev/null +++ b/module/plugins/accounts/UnrestrictLi.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class UnrestrictLi(Account): + __name__ = "UnrestrictLi" + __type__ = "account" + __version__ = "0.03" + + __description__ = """Unrestrict.li account plugin""" + __license__ = "GPLv3" + __authors__ = [("stickell", "l.stickell@yahoo.it")] + + + def loadAccountInfo(self, user, req): + json_data = req.load('http://unrestrict.li/api/jdownloader/user.php?format=json') + self.logDebug("JSON data: " + json_data) + json_data = json_loads(json_data) + + if 'vip' in json_data['result'] and json_data['result']['vip'] == 0: + return {"premium": False} + + validuntil = json_data['result']['expires'] + trafficleft = int(json_data['result']['traffic']) + + return {"premium": True, "validuntil": validuntil, "trafficleft": trafficleft} + + + def login(self, user, data, req): + req.cj.setCookie("unrestrict.li", "lang", "EN") + html = req.load("https://unrestrict.li/sign_in") + + if 'solvemedia' in html: + self.logError(_("A Captcha is required. Go to http://unrestrict.li/sign_in and login, then retry")) + return + + post_data = {"username": user, "password": data['password'], + "remember_me": "remember", "signin": "Sign in"} + html = req.load("https://unrestrict.li/sign_in", post=post_data) + + if 'sign_out' not in html: + self.wrongPassword() diff --git a/module/plugins/accounts/UploadcCom.py b/module/plugins/accounts/UploadcCom.py new file mode 100644 index 000000000..66863c456 --- /dev/null +++ b/module/plugins/accounts/UploadcCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class UploadcCom(XFSAccount): + __name__ = "UploadcCom" + __type__ = "account" + __version__ = "0.02" + + __description__ = """Uploadc.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + HOSTER_DOMAIN = "uploadc.com" diff --git a/module/plugins/accounts/UploadedTo.py b/module/plugins/accounts/UploadedTo.py new file mode 100644 index 000000000..91cdba2c1 --- /dev/null +++ b/module/plugins/accounts/UploadedTo.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +import re +from time import time + +from pyload.plugin.Account import Account + + +class UploadedTo(Account): + __name__ = "UploadedTo" + __type__ = "account" + __version__ = "0.27" + + __description__ = """Uploaded.to account plugin""" + __license__ = "GPLv3" + __authors__ = [("mkaay", "mkaay@mkaay.de")] + + + PREMIUM_PATTERN = r'<em>Premium</em>' + VALID_UNTIL_PATTERN = r'<td>Duration:</td>\s*<th>([^<]+)' + TRAFFIC_LEFT_PATTERN = r'<th colspan="2"><b class="cB">([^<]+)' + + + def loadAccountInfo(self, user, req): + validuntil = None + trafficleft = None + premium = None + + html = req.load("http://uploaded.net/me") + + premium = True if re.search(self.PREMIUM_PATTERN, html) else False + + m = re.search(self.VALID_UNTIL_PATTERN, html, re.M) + if m: + expiredate = m.group(1).strip() + + if expiredate == "unlimited": + validuntil = -1 + else: + m = re.findall(r'(\d+) (Week|weeks|day|hour)', expiredate) + if m: + validuntil = time() + for n, u in m: + validuntil += int(n) * 60 * 60 * {'Week': 168, 'weeks': 168, 'day': 24, 'hour': 1}[u] + + m = re.search(self.TRAFFIC_LEFT_PATTERN, html) + if m: + trafficleft = self.parseTraffic(m.group(1).replace('.', '')) + + return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium} + + + def login(self, user, data, req): + req.cj.setCookie("uploaded.net", "lang", "en") + + page = req.load("http://uploaded.net/io/login", + post={'id': user, 'pw': data['password'], '_': ""}) + + if "User and password do not match" in page: + self.wrongPassword() diff --git a/module/plugins/accounts/UploadheroCom.py b/module/plugins/accounts/UploadheroCom.py new file mode 100644 index 000000000..c7b05bc94 --- /dev/null +++ b/module/plugins/accounts/UploadheroCom.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +import re +import datetime +import time + +from pyload.plugin.Account import Account + + +class UploadheroCom(Account): + __name__ = "UploadheroCom" + __type__ = "account" + __version__ = "0.20" + + __description__ = """Uploadhero.co account plugin""" + __license__ = "GPLv3" + __authors__ = [("mcmyst", "mcmyst@hotmail.fr")] + + + def loadAccountInfo(self, user, req): + premium_pattern = re.compile('Il vous reste <span class="bleu">(\d+)</span> jours premium') + + data = self.getAccountData(user) + page = req.load("http://uploadhero.co/my-account") + + if premium_pattern.search(page): + end_date = datetime.date.today() + datetime.timedelta(days=int(premium_pattern.search(page).group(1))) + end_date = time.mktime(future.timetuple()) + account_info = {"validuntil": end_date, "trafficleft": -1, "premium": True} + else: + account_info = {"validuntil": -1, "trafficleft": -1, "premium": False} + + return account_info + + + def login(self, user, data, req): + page = req.load("http://uploadhero.co/lib/connexion.php", + post={"pseudo_login": user, "password_login": data['password']}) + + if "mot de passe invalide" in page: + self.wrongPassword() diff --git a/module/plugins/accounts/UploadingCom.py b/module/plugins/accounts/UploadingCom.py new file mode 100644 index 000000000..6d54469e8 --- /dev/null +++ b/module/plugins/accounts/UploadingCom.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +import re + +from time import time, strptime, mktime + +from pyload.plugin.Account import Account +from pyload.plugin.internal.SimpleHoster import set_cookies + + +class UploadingCom(Account): + __name__ = "UploadingCom" + __type__ = "account" + __version__ = "0.11" + + __description__ = """Uploading.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("mkaay", "mkaay@mkaay.de")] + + + PREMIUM_PATTERN = r'UPGRADE TO PREMIUM' + VALID_UNTIL_PATTERN = r'Valid Until:(.+?)<' + + + def loadAccountInfo(self, user, req): + validuntil = None + trafficleft = None + premium = None + + html = req.load("http://uploading.com/") + + premium = False if re.search(self.PREMIUM_PATTERN, html) else True + + m = re.search(self.VALID_UNTIL_PATTERN, html) + if m: + expiredate = m.group(1).strip() + self.logDebug("Expire date: " + expiredate) + + try: + validuntil = mktime(strptime(expiredate, "%b %d, %Y")) + + except Exception, e: + self.logError(e) + + else: + if validuntil > mktime(gmtime()): + premium = True + else: + premium = False + validuntil = None + + return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium} + + + def login(self, user, data, req): + set_cookies([("uploading.com", "lang", "1"), + ("uploading.com", "language", "1"), + ("uploading.com", "setlang", "en"), + ("uploading.com", "_lang", "en")] + + req.load("http://uploading.com/") + req.load("http://uploading.com/general/login_form/?JsHttpRequest=%s-xml" % long(time() * 1000), + post={'email': user, 'password': data['password'], 'remember': "on"}) diff --git a/module/plugins/accounts/UptoboxCom.py b/module/plugins/accounts/UptoboxCom.py new file mode 100644 index 000000000..2d0b49957 --- /dev/null +++ b/module/plugins/accounts/UptoboxCom.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class UptoboxCom(XFSAccount): + __name__ = "UptoboxCom" + __type__ = "account" + __version__ = "0.07" + + __description__ = """DDLStorage.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + HOSTER_DOMAIN = "uptobox.com" + HOSTER_URL = "https://uptobox.com/" diff --git a/module/plugins/accounts/VidPlayNet.py b/module/plugins/accounts/VidPlayNet.py new file mode 100644 index 000000000..390520a00 --- /dev/null +++ b/module/plugins/accounts/VidPlayNet.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class VidPlayNet(XFSAccount): + __name__ = "VidPlayNet" + __type__ = "account" + __version__ = "0.02" + + __description__ = """VidPlay.net account plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + HOSTER_DOMAIN = "vidplay.net" diff --git a/module/plugins/accounts/XFileSharingPro.py b/module/plugins/accounts/XFileSharingPro.py new file mode 100644 index 000000000..9d938c4c5 --- /dev/null +++ b/module/plugins/accounts/XFileSharingPro.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class XFileSharingPro(XFSAccount): + __name__ = "XFileSharingPro" + __type__ = "account" + __version__ = "0.05" + + __description__ = """XFileSharingPro multi-purpose account plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + HOSTER_DOMAIN = None + + + def init(self): + if self.HOSTER_DOMAIN: + return super(XFileSharingPro, self).init() + + + def loadAccountInfo(self, user, req): + return super(XFileSharingPro if self.HOSTER_DOMAIN else XFSAccount, self).loadAccountInfo(user, req) + + + def login(self, user, data, req): + if self.HOSTER_DOMAIN: + return super(XFileSharingPro, self).login(user, data, req) diff --git a/module/plugins/accounts/YibaishiwuCom.py b/module/plugins/accounts/YibaishiwuCom.py new file mode 100644 index 000000000..e169a5901 --- /dev/null +++ b/module/plugins/accounts/YibaishiwuCom.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Account import Account + + +class YibaishiwuCom(Account): + __name__ = "YibaishiwuCom" + __type__ = "account" + __version__ = "0.01" + + __description__ = """115.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + ACCOUNT_INFO_PATTERN = r'var USER_PERMISSION = {(.*?)}' + + + def loadAccountInfo(self, user, req): + #self.relogin(user) + html = req.load("http://115.com/", decode=True) + + m = re.search(self.ACCOUNT_INFO_PATTERN, html, re.S) + premium = True if (m and 'is_vip: 1' in m.group(1)) else False + validuntil = trafficleft = (-1 if m else 0) + return dict({"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium}) + + + def login(self, user, data, req): + html = req.load('http://passport.115.com/?ac=login', post={ + "back": "http://www.115.com/", + "goto": "http://115.com/", + "login[account]": user, + "login[passwd]": data['password'] + }, decode=True) + + if not 'var USER_PERMISSION = {' in html: + self.wrongPassword() diff --git a/module/plugins/accounts/ZeveraCom.py b/module/plugins/accounts/ZeveraCom.py new file mode 100644 index 000000000..2eee62ac1 --- /dev/null +++ b/module/plugins/accounts/ZeveraCom.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +from time import mktime, strptime + +from pyload.plugin.Account import Account + + +class ZeveraCom(Account): + __name__ = "ZeveraCom" + __type__ = "account" + __version__ = "0.21" + + __description__ = """Zevera.com account plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + def loadAccountInfo(self, user, req): + data = self.getAPIData(req) + if data == "No traffic": + account_info = {"trafficleft": 0, "validuntil": 0, "premium": False} + else: + account_info = { + "trafficleft": int(data['availabletodaytraffic']) * 1024, + "validuntil": mktime(strptime(data['endsubscriptiondate'], "%Y/%m/%d %H:%M:%S")), + "premium": True + } + return account_info + + + def login(self, user, data, req): + self.loginname = user + self.password = data['password'] + if self.getAPIData(req) == "No traffic": + self.wrongPassword() + + + def getAPIData(self, req, just_header=False, **kwargs): + get_data = { + 'cmd': 'accountinfo', + 'login': self.loginname, + 'pass': self.password + } + get_data.update(kwargs) + + res = req.load("http://www.zevera.com/jDownloader.ashx", get=get_data, + decode=True, just_header=just_header) + self.logDebug(res) + + if ':' in res: + if not just_header: + res = res.replace(',', '\n') + return dict((y.strip().lower(), z.strip()) for (y, z) in + [x.split(':', 1) for x in res.splitlines() if ':' in x]) + else: + return res diff --git a/module/plugins/accounts/__init__.py b/module/plugins/accounts/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/module/plugins/accounts/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/module/plugins/addon/Checksum.py b/module/plugins/addon/Checksum.py new file mode 100644 index 000000000..0589bd55a --- /dev/null +++ b/module/plugins/addon/Checksum.py @@ -0,0 +1,186 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import hashlib +import re +import zlib + +from os import remove +from os.path import getsize, isfile, splitext + +from pyload.plugin.Addon import Addon +from pyload.utils import safe_join, fs_encode + + +def computeChecksum(local_file, algorithm): + if algorithm in getattr(hashlib, "algorithms", ("md5", "sha1", "sha224", "sha256", "sha384", "sha512")): + h = getattr(hashlib, algorithm)() + + with open(local_file, 'rb') as f: + for chunk in iter(lambda: f.read(128 * h.block_size), ''): + h.update(chunk) + + return h.hexdigest() + + elif algorithm in ("adler32", "crc32"): + hf = getattr(zlib, algorithm) + last = 0 + + with open(local_file, 'rb') as f: + for chunk in iter(lambda: f.read(8192), ''): + last = hf(chunk, last) + + return "%x" % last + + else: + return None + + +class Checksum(Addon): + __name__ = "Checksum" + __type__ = "addon" + __version__ = "0.15" + + __config__ = [("activated" , "bool" , "Activated" , True ), + ("check_checksum", "bool" , "Check checksum? (If False only size will be verified)", True ), + ("check_action" , "fail;retry;nothing", "What to do if check fails?" , "retry"), + ("max_tries" , "int" , "Number of retries" , 2 ), + ("retry_action" , "fail;nothing" , "What to do if all retries fail?" , "fail" ), + ("wait_time" , "int" , "Time to wait before each retry (seconds)" , 1 )] + + __description__ = """Verify downloaded file size and checksum""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), + ("Walter Purcaro", "vuolter@gmail.com"), + ("stickell", "l.stickell@yahoo.it")] + + + methods = {'sfv': 'crc32', 'crc': 'crc32', 'hash': 'md5'} + regexps = {'sfv': r'^(?P<name>[^;].+)\s+(?P<hash>[0-9A-Fa-f]{8})$', + 'md5': r'^(?P<name>[0-9A-Fa-f]{32}) (?P<file>.+)$', + 'crc': r'filename=(?P<name>.+)\nsize=(?P<size>\d+)\ncrc32=(?P<hash>[0-9A-Fa-f]{8})$', + 'default': r'^(?P<hash>[0-9A-Fa-f]+)\s+\*?(?P<name>.+)$'} + + + def activate(self): + if not self.getConfig("check_checksum"): + self.logInfo(_("Checksum validation is disabled in plugin configuration")) + + + def setup(self): + self.algorithms = sorted( + getattr(hashlib, "algorithms", ("md5", "sha1", "sha224", "sha256", "sha384", "sha512")), reverse=True) + self.algorithms.extend(["crc32", "adler32"]) + self.formats = self.algorithms + ["sfv", "crc", "hash"] + + + def downloadFinished(self, pyfile): + """ + Compute checksum for the downloaded file and compare it with the hash provided by the hoster. + pyfile.plugin.check_data should be a dictionary which can contain: + a) if known, the exact filesize in bytes (e.g. "size": 123456789) + b) hexadecimal hash string with algorithm name as key (e.g. "md5": "d76505d0869f9f928a17d42d66326307") + """ + if hasattr(pyfile.plugin, "check_data") and isinstance(pyfile.plugin.check_data, dict): + data = pyfile.plugin.check_data.copy() + + elif hasattr(pyfile.plugin, "api_data") and isinstance(pyfile.plugin.api_data, dict): + data = pyfile.plugin.api_data.copy() + + # elif hasattr(pyfile.plugin, "info") and isinstance(pyfile.plugin.info, dict): + # data = pyfile.plugin.info.copy() + + else: + return + + self.logDebug(data) + + if not pyfile.plugin.lastDownload: + self.checkFailed(pyfile, None, "No file downloaded") + + local_file = fs_encode(pyfile.plugin.lastDownload) + #download_folder = self.config['general']['download_folder'] + #local_file = fs_encode(safe_join(download_folder, pyfile.package().folder, pyfile.name)) + + if not isfile(local_file): + self.checkFailed(pyfile, None, "File does not exist") + + # validate file size + if "size" in data: + api_size = int(data['size']) + file_size = getsize(local_file) + if api_size != file_size: + self.logWarning(_("File %s has incorrect size: %d B (%d expected)") % (pyfile.name, file_size, api_size)) + self.checkFailed(pyfile, local_file, "Incorrect file size") + del data['size'] + + # validate checksum + if data and self.getConfig("check_checksum"): + if "checksum" in data: + data['md5'] = data['checksum'] + + for key in self.algorithms: + if key in data: + checksum = computeChecksum(local_file, key.replace("-", "").lower()) + if checksum: + if checksum == data[key].lower(): + self.logInfo(_('File integrity of "%s" verified by %s checksum (%s)') % + (pyfile.name, key.upper(), checksum)) + break + else: + self.logWarning(_("%s checksum for file %s does not match (%s != %s)") % + (key.upper(), pyfile.name, checksum, data[key])) + self.checkFailed(pyfile, local_file, "Checksums do not match") + else: + self.logWarning(_("Unsupported hashing algorithm"), key.upper()) + else: + self.logWarning(_("Unable to validate checksum for file: ") + pyfile.name) + + + def checkFailed(self, pyfile, local_file, msg): + check_action = self.getConfig("check_action") + if check_action == "retry": + max_tries = self.getConfig("max_tries") + retry_action = self.getConfig("retry_action") + if pyfile.plugin.retries < max_tries: + if local_file: + remove(local_file) + pyfile.plugin.retry(max_tries, self.getConfig("wait_time"), msg) + elif retry_action == "nothing": + return + elif check_action == "nothing": + return + pyfile.plugin.fail(reason=msg) + + + def packageFinished(self, pypack): + download_folder = safe_join(self.config['general']['download_folder'], pypack.folder, "") + + for link in pypack.getChildren().itervalues(): + file_type = splitext(link['name'])[1][1:].lower() + + if file_type not in self.formats: + continue + + hash_file = fs_encode(safe_join(download_folder, link['name'])) + if not isfile(hash_file): + self.logWarning(_("File not found"), link['name']) + continue + + with open(hash_file) as f: + text = f.read() + + for m in re.finditer(self.regexps.get(file_type, self.regexps['default']), text): + data = m.groupdict() + self.logDebug(link['name'], data) + + local_file = fs_encode(safe_join(download_folder, data['name'])) + algorithm = self.methods.get(file_type, file_type) + checksum = computeChecksum(local_file, algorithm) + if checksum == data['hash']: + self.logInfo(_('File integrity of "%s" verified by %s checksum (%s)') % + (data['name'], algorithm, checksum)) + else: + self.logWarning(_("%s checksum for file %s does not match (%s != %s)") % + (algorithm, data['name'], checksum, data['hash'])) diff --git a/module/plugins/addon/ClickAndLoad.py b/module/plugins/addon/ClickAndLoad.py new file mode 100644 index 000000000..5fe6e4bec --- /dev/null +++ b/module/plugins/addon/ClickAndLoad.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +from socket import socket, error +from threading import Thread + +from pyload.plugin.Addon import Addon + + +def forward(source, destination): + string = ' ' + while string: + string = source.recv(1024) + if string: + destination.sendall(string) + else: + #source.shutdown(socket.SHUT_RD) + destination.shutdown(socket.SHUT_WR) + + +class ClickAndLoad(Addon): + __name__ = "ClickAndLoad" + __type__ = "addon" + __version__ = "0.23" + + __config__ = [("activated", "bool", "Activated" , True ), + ("port" , "int" , "Port" , 9666 ), + ("extern" , "bool", "Allow external link adding", False)] + + __description__ = """Click'N'Load hook plugin""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.de"), + ("mkaay", "mkaay@mkaay.de"), + ("Walter Purcaro", "vuolter@gmail.com")] + + + def setup(self): + self.interval = 300 + + + def activate(self): + self.initPeriodical() + + + def periodical(self): + webip = "0.0.0.0" if self.getConfig("extern") else "127.0.0.1" + webport = self.config['webinterface']['port'] + cnlport = self.getConfig("port")) + + try: + s = socket() + s.bind((webip, cnlport)) + s.listen(5) + + client = s.accept()[0] + server = socket() + + server.connect(("127.0.0.1", webport)) + + except error, e: + if hasattr(e, "errno"): + errno = e.errno + else: + errno = e.args[0] + + if errno == 98: + self.logWarning(_("Port %d already in use") % cnlport) + else: + self.logDebug(e) + + else: + self.core.scheduler.removeJob(self.cb) + t = Thread(target=forward, args=[client, server]) + t.setDaemon(True) + t.start() diff --git a/module/plugins/addon/DeleteFinished.py b/module/plugins/addon/DeleteFinished.py new file mode 100644 index 000000000..59f2e3321 --- /dev/null +++ b/module/plugins/addon/DeleteFinished.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- + +from pyload.database import style +from pyload.plugin.Addon import Addon + + +class DeleteFinished(Addon): + __name__ = "DeleteFinished" + __type__ = "addon" + __version__ = "1.11" + + __config__ = [('interval' , 'int' , 'Delete every (hours)' , '72' ), + ('deloffline', 'bool', 'Delete packages with offline links', 'False')] + + __description__ = """Automatically delete all finished packages from queue""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + # event_list = ["pluginConfigChanged"] + + + ## overwritten methods ## + def periodical(self): + if not self.info['sleep']: + deloffline = self.getConfig('deloffline') + mode = '0,1,4' if deloffline else '0,4' + msg = _('delete all finished packages in queue list (%s packages with offline links)') + self.logInfo(msg % (_('including') if deloffline else _('excluding'))) + self.deleteFinished(mode) + self.info['sleep'] = True + self.addEvent('packageFinished', self.wakeup) + + + def pluginConfigChanged(self, plugin, name, value): + if name == "interval" and value != self.interval: + self.interval = value * 3600 + self.initPeriodical() + + + def deactivate(self): + self.removeEvent('packageFinished', self.wakeup) + + + def activate(self): + self.info = {'sleep': True} + interval = self.getConfig('interval') + self.pluginConfigChanged(self.__name__, 'interval', interval) + self.addEvent('packageFinished', self.wakeup) + + + ## own methods ## + @style.queue + def deleteFinished(self, mode): + self.c.execute('DELETE FROM packages WHERE NOT EXISTS(SELECT 1 FROM links WHERE package=packages.id AND status NOT IN (%s))' % mode) + self.c.execute('DELETE FROM links WHERE NOT EXISTS(SELECT 1 FROM packages WHERE id=links.package)') + + + def wakeup(self, pypack): + self.removeEvent('packageFinished', self.wakeup) + self.info['sleep'] = False + + + ## event managing ## + def addEvent(self, event, func): + """Adds an event listener for event name""" + if event in self.m.events: + if func in self.m.events[event]: + self.logDebug("Function already registered", func) + else: + self.m.events[event].append(func) + else: + self.m.events[event] = [func] + + + def setup(self): + self.interval = 0 + self.m = self.manager + self.removeEvent = self.m.removeEvent diff --git a/module/plugins/addon/DownloadScheduler.py b/module/plugins/addon/DownloadScheduler.py new file mode 100644 index 000000000..e5e25e389 --- /dev/null +++ b/module/plugins/addon/DownloadScheduler.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- + +import re + +from time import localtime + +from pyload.plugin.Addon import Addon + + +class DownloadScheduler(Addon): + __name__ = "DownloadScheduler" + __type__ = "addon" + __version__ = "0.22" + + __config__ = [("timetable", "str" , "List time periods as hh:mm full or number(kB/s)" , "0:00 full, 7:00 250, 10:00 0, 17:00 150"), + ("abort" , "bool", "Abort active downloads when start period with speed 0", False )] + + __description__ = """Download Scheduler""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), + ("stickell", "l.stickell@yahoo.it")] + + + def setup(self): + self.cb = None #: callback to scheduler job; will be by removed AddonManager when addon unloaded + + + def activate(self): + self.updateSchedule() + + + def updateSchedule(self, schedule=None): + if schedule is None: + schedule = self.getConfig("timetable") + + schedule = re.findall("(\d{1,2}):(\d{2})[\s]*(-?\d+)", + schedule.lower().replace("full", "-1").replace("none", "0")) + if not schedule: + self.logError(_("Invalid schedule")) + return + + t0 = localtime() + now = (t0.tm_hour, t0.tm_min, t0.tm_sec, "X") + schedule = sorted([(int(x[0]), int(x[1]), 0, int(x[2])) for x in schedule] + [now]) + + self.logDebug("Schedule", schedule) + + for i, v in enumerate(schedule): + if v[3] == "X": + last, next = schedule[i - 1], schedule[(i + 1) % len(schedule)] + self.logDebug("Now/Last/Next", now, last, next) + + self.setDownloadSpeed(last[3]) + + next_time = (((24 + next[0] - now[0]) * 60 + next[1] - now[1]) * 60 + next[2] - now[2]) % 86400 + self.core.scheduler.removeJob(self.cb) + self.cb = self.core.scheduler.addJob(next_time, self.updateSchedule, threaded=False) + + + def setDownloadSpeed(self, speed): + if speed == 0: + abort = self.getConfig("abort") + self.logInfo(_("Stopping download server. (Running downloads will %sbe aborted.)") % '' if abort else _('not ')) + self.core.api.pauseServer() + if abort: + self.core.api.stopAllDownloads() + else: + self.core.api.unpauseServer() + + if speed > 0: + self.logInfo(_("Setting download speed to %d kB/s") % speed) + self.core.api.setConfigValue("download", "limit_speed", 1) + self.core.api.setConfigValue("download", "max_speed", speed) + else: + self.logInfo(_("Setting download speed to FULL")) + self.core.api.setConfigValue("download", "limit_speed", 0) + self.core.api.setConfigValue("download", "max_speed", -1) diff --git a/module/plugins/addon/ExternalScripts.py b/module/plugins/addon/ExternalScripts.py new file mode 100644 index 000000000..31283afc2 --- /dev/null +++ b/module/plugins/addon/ExternalScripts.py @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- + +import subprocess + +from itertools import chain +from os import listdir, access, X_OK, makedirs +from os.path import join, exists, basename, abspath + +from pyload.plugin.Addon import Addon +from pyload.utils import safe_join + + +class ExternalScripts(Addon): + __name__ = "ExternalScripts" + __type__ = "addon" + __version__ = "0.25" + + __config__ = [("activated", "bool", "Activated", True)] + + __description__ = """Run external scripts""" + __license__ = "GPLv3" + __authors__ = [("mkaay", "mkaay@mkaay.de"), + ("RaNaN", "ranan@pyload.org"), + ("spoob", "spoob@pyload.org"), + ("Walter Purcaro", "vuolter@gmail.com")] + + + event_map = {'archive-extracted' : "archive_extracted", + 'package-extracted' : "package_extracted", + 'all_archives-extracted' : "all_archives_extracted", + 'all_archives-processed' : "all_archives_processed", + 'all_downloads-finished' : "allDownloadsFinished", + 'all_downloads-processed': "allDownloadsProcessed"} + + + def setup(self): + self.scripts = {} + + folders = ["download_preparing", "download_finished", "all_downloads_finished", "all_downloads_processed", + "before_reconnect", "after_reconnect", + "package_finished", "package_extracted", + "archive_extracted", "all_archives_extracted", "all_archives_processed", + # deprecated folders + "unrar_finished", "all_dls_finished", "all_dls_processed"] + + for folder in folders: + self.scripts[folder] = [] + + self.initPluginType(folder, join(pypath, 'scripts', folder)) + self.initPluginType(folder, join('scripts', folder)) + + for script_type, names in self.scripts.iteritems(): + if names: + self.logInfo(_("Installed scripts for"), script_type, ", ".join([basename(x) for x in names])) + + + def initPluginType(self, folder, path): + if not exists(path): + try: + makedirs(path) + except Exception: + self.logDebug("Script folder %s not created" % folder) + return + + for f in listdir(path): + if f.startswith("#") or f.startswith(".") or f.startswith("_") or f.endswith("~") or f.endswith(".swp"): + continue + + if not access(join(path, f), X_OK): + self.logWarning(_("Script not executable:") + " %s/%s" % (folder, f)) + + self.scripts[folder].append(join(path, f)) + + + def callScript(self, script, *args): + try: + cmd = [script] + [str(x) if not isinstance(x, basestring) else x for x in args] + self.logDebug("Executing", abspath(script), " ".join(cmd)) + #output goes to pyload + subprocess.Popen(cmd, bufsize=-1) + except Exception, e: + self.logError(_("Error in %(script)s: %(error)s") % {"script": basename(script), "error": e}) + + + def downloadPreparing(self, pyfile): + for script in self.scripts['download_preparing']: + self.callScript(script, pyfile.pluginname, pyfile.url, pyfile.id) + + + def downloadFinished(self, pyfile): + download_folder = self.config['general']['download_folder'] + for script in self.scripts['download_finished']: + filename = safe_join(download_folder, pyfile.package().folder, pyfile.name) + self.callScript(script, pyfile.pluginname, pyfile.url, pyfile.name, filename, pyfile.id) + + + def packageFinished(self, pypack): + download_folder = self.config['general']['download_folder'] + for script in self.scripts['package_finished']: + folder = safe_join(download_folder, pypack.folder) + self.callScript(script, pypack.name, folder, pypack.password, pypack.id) + + + def beforeReconnecting(self, ip): + for script in self.scripts['before_reconnect']: + self.callScript(script, ip) + + + def afterReconnecting(self, ip): + for script in self.scripts['after_reconnect']: + self.callScript(script, ip) + + + def archive_extracted(self, pyfile, folder, filename, files): + for script in self.scripts['archive_extracted']: + self.callScript(script, folder, filename, files) + for script in self.scripts['unrar_finished']: #: deprecated + self.callScript(script, folder, filename) + + + def package_extracted(self, pypack): + download_folder = self.config['general']['download_folder'] + for script in self.scripts['package_extracted']: + folder = safe_join(download_folder, pypack.folder) + self.callScript(script, pypack.name, folder, pypack.password, pypack.id) + + + def all_archives_extracted(self): + for script in self.scripts['all_archives_extracted']: + self.callScript(script) + + + def all_archives_processed(self): + for script in self.scripts['all_archives_processed']: + self.callScript(script) + + + def allDownloadsFinished(self): + for script in chain(self.scripts['all_downloads_finished'], self.scripts['all_dls_finished']): + self.callScript(script) + + + def allDownloadsProcessed(self): + for script in chain(self.scripts['all_downloads_processed'], self.scripts['all_dls_processed']): + self.callScript(script) diff --git a/module/plugins/addon/ExtractArchive.py b/module/plugins/addon/ExtractArchive.py new file mode 100644 index 000000000..b24bb37a2 --- /dev/null +++ b/module/plugins/addon/ExtractArchive.py @@ -0,0 +1,363 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import os +import sys + +from copy import copy +from os import remove, chmod, makedirs +from os.path import exists, basename, isfile, isdir +from traceback import print_exc + +# monkey patch bug in python 2.6 and lower +# http://bugs.python.org/issue6122 , http://bugs.python.org/issue1236 , http://bugs.python.org/issue1731717 +if sys.version_info < (2, 7) and os.name != "nt": + import errno + from subprocess import Popen + + + def _eintr_retry_call(func, *args): + while True: + try: + return func(*args) + except OSError, e: + if e.errno == errno.EINTR: + continue + raise + + + # unsued timeout option for older python version + def wait(self, timeout=0): + """Wait for child process to terminate. Returns returncode + attribute.""" + if self.returncode is None: + try: + pid, sts = _eintr_retry_call(os.waitpid, self.pid, 0) + except OSError, e: + if e.errno != errno.ECHILD: + raise + # This happens if SIGCLD is set to be ignored or waiting + # for child processes has otherwise been disabled for our + # process. This child is dead, we can't get the status. + sts = 0 + self._handle_exitstatus(sts) + return self.returncode + + Popen.wait = wait + +if os.name != "nt": + from grp import getgrnam + from os import chown + from pwd import getpwnam + +from pyload.plugin.Addon import Addon, threaded, Expose +from pyload.plugin.internal.AbstractExtractor import ArchiveError, CRCError, WrongPassword +from pyload.utils import safe_join, fs_encode + + +class ExtractArchive(Addon): + __name__ = "ExtractArchive" + __type__ = "addon" + __version__ = "0.19" + + __config__ = [("activated" , "bool" , "Activated" , True ), + ("fullpath" , "bool" , "Extract full path" , True ), + ("overwrite" , "bool" , "Overwrite files" , True ), + ("passwordfile" , "file" , "password file" , "archive_password.txt"), + ("deletearchive", "bool" , "Delete archives when done" , False ), + ("subfolder" , "bool" , "Create subfolder for each package" , False ), + ("destination" , "folder", "Extract files to" , "" ), + ("excludefiles" , "str" , "Exclude files from unpacking (seperated by ;)", "" ), + ("recursive" , "bool" , "Extract archives in archvies" , True ), + ("queue" , "bool" , "Wait for all downloads to be finished" , True ), + ("renice" , "int" , "CPU Priority" , 0 )] + + __description__ = """Extract different kind of archives""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "ranan@pyload.org"), + ("AndroKev", ""), + ("Walter Purcaro", "vuolter@gmail.com")] + + + event_map = {'all_downloads-processed': "allDownloadsProcessed"} + + + def setup(self): + self.plugins = [] + self.passwords = [] + names = [] + + for p in ("UnRar", "UnZip"): + try: + module = self.core.pluginManager.loadModule("internal", p) + klass = getattr(module, p) + if klass.checkDeps(): + names.append(p) + self.plugins.append(klass) + + except OSError, e: + if e.errno == 2: + self.logInfo(_("No %s installed") % p) + else: + self.logWarning(_("Could not activate %s") % p, e) + if self.core.debug: + print_exc() + + except Exception, e: + self.logWarning(_("Could not activate %s") % p, e) + if self.core.debug: + print_exc() + + if names: + self.logInfo(_("Activated") + " " + " ".join(names)) + else: + self.logInfo(_("No Extract plugins activated")) + + # queue with package ids + self.queue = [] + + + @Expose + def extractPackage(self, id): + """ Extract package with given id""" + self.manager.startThread(self.extract, [id]) + + + def packageFinished(self, pypack): + pid = pypack.id + if self.getConfig("queue"): + self.logInfo(_("Package %s queued for later extracting") % pypack.name) + self.queue.append(pid) + else: + self.manager.startThread(self.extract, [pid]) + + + @threaded + def allDownloadsProcessed(self, thread): + local = copy(self.queue) + del self.queue[:] + if self.extract(local, thread): #: check only if all gone fine, no failed reporting for now + self.manager.dispatchEvent("all_archives-extracted") + self.manager.dispatchEvent("all_archives-processed") + + + def extract(self, ids, thread=None): + processed = [] + extracted = [] + failed = [] + + destination = self.getConfig("destination") + subfolder = self.getConfig("subfolder") + fullpath = self.getConfig("fullpath") + overwrite = self.getConfig("overwrite") + excludefiles = self.getConfig("excludefiles") + renice = self.getConfig("renice") + recursive = self.getConfig("recursive") + + # reload from txt file + self.reloadPasswords() + + # dl folder + dl = self.config['general']['download_folder'] + + #iterate packages -> plugins -> targets + for pid in ids: + p = self.core.files.getPackage(pid) + self.logInfo(_("Check package %s") % p.name) + if not p: + continue + + # determine output folder + out = safe_join(dl, p.folder, "") + + out = safe_join(dl, p.folder, self.getConfig("destination"), "") + if subfolder: + out = safe_join(out, fs_encode(p.folder)) + + if not exists(out): + makedirs(out) + + files_ids = [(safe_join(dl, p.folder, x['name']), x['id']) for x in p.getChildren().itervalues()] + matched = False + success = True + + # check as long there are unseen files + while files_ids: + new_files_ids = [] + + for plugin in self.plugins: + targets = plugin.getTargets(files_ids) + if targets: + self.logDebug("Targets for %s: %s" % (plugin.__name__, targets)) + matched = True + for target, fid in targets: + if target in processed: + self.logDebug(basename(target), "skipped") + continue + + processed.append(target) # prevent extracting same file twice + + self.logInfo(basename(target), _("Extract to %s") % out) + try: + klass = plugin(self, target, out, fullpath, overwrite, excludefiles, renice) + klass.init() + password = p.password.strip().splitlines() + new_files = self._extract(klass, fid, password, thread) + except Exception, e: + self.logError(basename(target), e) + success = False + continue + + self.logDebug("Extracted", new_files) + self.setPermissions(new_files) + + for file in new_files: + if not exists(file): + self.logDebug("New file %s does not exists" % file) + continue + if recursive and isfile(file): + new_files_ids.append((file, fid)) # append as new target + + files_ids = new_files_ids # also check extracted files + + if matched: + if success: + extracted.append(pid) + self.manager.dispatchEvent("package-extracted", p) + else: + failed.append(pid) + self.manager.dispatchEvent("package-extract_failed", p) + else: + self.logInfo(_("No files found to extract")) + + return True if not failed else False + + + def _extract(self, plugin, fid, passwords, thread): + pyfile = self.core.files.getFile(fid) + deletearchive = self.getConfig("deletearchive") + + pyfile.setCustomStatus(_("extracting")) + thread.addActive(pyfile) # keep this file until everything is done + + try: + progress = lambda x: pyfile.setProgress(x) + success = False + + if not plugin.checkArchive(): + plugin.extract(progress) + success = True + else: + self.logInfo(basename(plugin.file), _("Password protected")) + self.logDebug("Passwords", passwords) + + pwlist = copy(self.getPasswords()) + # remove already supplied pws from list (only local) + for pw in passwords: + if pw in pwlist: + pwlist.remove(pw) + + for pw in passwords + pwlist: + try: + self.logDebug("Try password", pw) + if plugin.checkPassword(pw): + plugin.extract(progress, pw) + self.addPassword(pw) + success = True + break + except WrongPassword: + self.logDebug("Password was wrong") + + if not success: + raise Exception(_("Wrong password")) + + if self.core.debug: + self.logDebug("Would delete", ", ".join(plugin.getDeleteFiles())) + + if deletearchive: + files = plugin.getDeleteFiles() + self.logInfo(_("Deleting %s files") % len(files)) + for f in files: + if exists(f): + remove(f) + else: + self.logDebug("%s does not exists" % f) + + self.logInfo(basename(plugin.file), _("Extracting finished")) + + extracted_files = plugin.getExtractedFiles() + self.manager.dispatchEvent("archive-extracted", pyfile, plugin.out, plugin.file, extracted_files) + + return extracted_files + + except ArchiveError, e: + self.logError(basename(plugin.file), _("Archive Error"), e) + except CRCError: + self.logError(basename(plugin.file), _("CRC Mismatch")) + except Exception, e: + if self.core.debug: + print_exc() + self.logError(basename(plugin.file), _("Unknown Error"), e) + + self.manager.dispatchEvent("archive-extract_failed", pyfile) + raise Exception(_("Extract failed")) + + + @Expose + def getPasswords(self): + """ List of saved passwords """ + return self.passwords + + + def reloadPasswords(self): + passwordfile = self.getConfig("passwordfile") + + try: + passwords = [] + with open(passwordfile, "a+") as f: + for pw in f.read().splitlines(): + passwords.append(pw) + + except IOError, e: + self.logError(e) + + else: + self.passwords = passwords + + + @Expose + def addPassword(self, pw): + """ Adds a password to saved list""" + passwordfile = self.getConfig("passwordfile") + + if pw in self.passwords: + self.passwords.remove(pw) + + self.passwords.insert(0, pw) + + try: + with open(passwordfile, "wb") as f: + for pw in self.passwords: + f.write(pw + "\n") + except IOError, e: + self.logError(e) + + + def setPermissions(self, files): + for f in files: + if not exists(f): + continue + try: + if self.config['permission']['change_file']: + if isfile(f): + chmod(f, int(self.config['permission']['file'], 8)) + elif isdir(f): + chmod(f, int(self.config['permission']['folder'], 8)) + + if self.config['permission']['change_dl'] and os.name != "nt": + uid = getpwnam(self.config['permission']['user'])[2] + gid = getgrnam(self.config['permission']['group'])[2] + chown(f, uid, gid) + except Exception, e: + self.logWarning(_("Setting User and Group failed"), e) diff --git a/module/plugins/addon/HotFolder.py b/module/plugins/addon/HotFolder.py new file mode 100644 index 000000000..3bbafd5ed --- /dev/null +++ b/module/plugins/addon/HotFolder.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import time + +from os import listdir, makedirs +from os.path import exists, isfile, join +from shutil import move + +from pyload.plugin.Addon import Addon +from pyload.utils import fs_encode, safe_join + + +class HotFolder(Addon): + __name__ = "HotFolder" + __type__ = "addon" + __version__ = "0.12" + + __config__ = [("folder" , "str" , "Folder to observe" , "container"), + ("watch_file", "bool", "Observe link file" , False ), + ("keep" , "bool", "Keep added containers", True ), + ("file" , "str" , "Link file" , "links.txt")] + + __description__ = """Observe folder and file for changes and add container and links""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.de")] + + + def setup(self): + self.interval = 10 + + + def activate(self): + self.initPeriodical() + + + def periodical(self): + folder = fs_encode(self.getConfig("folder")) + + try: + if not exists(join(folder, "finished")): + makedirs(join(folder, "finished")) + + if self.getConfig("watch_file"): + with open(fs_encode(self.getConfig("file")), "a+") as f: + content = f.read().strip() + + if content: + name = "%s_%s.txt" % (self.getConfig("file"), time.strftime("%H-%M-%S_%d%b%Y")) + + with open(safe_join(folder, "finished", name), "wb") as f: + f.write(content) + + self.core.api.addPackage(f.name, [f.name], 1) + + for f in listdir(folder): + path = join(folder, f) + + if not isfile(path) or f.endswith("~") or f.startswith("#") or f.startswith("."): + continue + + newpath = join(folder, "finished", f if self.getConfig("keep") else "tmp_" + f) + move(path, newpath) + + self.logInfo(_("Added %s from HotFolder") % f) + self.core.api.addPackage(f, [newpath], 1) + + except IOError, e: + self.logError(e) diff --git a/module/plugins/addon/IRCInterface.py b/module/plugins/addon/IRCInterface.py new file mode 100644 index 000000000..a4d466319 --- /dev/null +++ b/module/plugins/addon/IRCInterface.py @@ -0,0 +1,431 @@ +# -*- coding: utf-8 -*- + +import re +import socket +import ssl +import time + +from pycurl import FORM_FILE +from select import select +from threading import Thread +from time import sleep +from traceback import print_exc + +from pyload.api import PackageDoesNotExists, FileDoesNotExists +from pyload.network.RequestFactory import getURL +from pyload.plugin.Addon import Addon +from pyload.utils import formatSize + + +class IRCInterface(Thread, Addon): + __name__ = "IRCInterface" + __type__ = "addon" + __version__ = "0.13" + + __config__ = [("host" , "str" , "IRC-Server Address" , "Enter your server here!"), + ("port" , "int" , "IRC-Server Port" , 6667 ), + ("ident" , "str" , "Clients ident" , "pyload-irc" ), + ("realname" , "str" , "Realname" , "pyload-irc" ), + ("ssl" , "bool", "Use SSL" , False ), + ("nick" , "str" , "Nickname the Client will take" , "pyLoad-IRC" ), + ("owner" , "str" , "Nickname the Client will accept commands from", "Enter your nick here!" ), + ("info_file", "bool", "Inform about every file finished" , False ), + ("info_pack", "bool", "Inform about every package finished" , True ), + ("captcha" , "bool", "Send captcha requests" , True )] + + __description__ = """Connect to irc and let owner perform different tasks""" + __license__ = "GPLv3" + __authors__ = [("Jeix", "Jeix@hasnomail.com")] + + + def __init__(self, core, manager): + Thread.__init__(self) + Addon.__init__(self, core, manager) + self.setDaemon(True) + + + def activate(self): + self.abort = False + self.more = [] + self.new_package = {} + + self.start() + + + def packageFinished(self, pypack): + try: + if self.getConfig("info_pack"): + self.response(_("Package finished: %s") % pypack.name) + except Exception: + pass + + + def downloadFinished(self, pyfile): + try: + if self.getConfig("info_file"): + self.response( + _("Download finished: %(name)s @ %(plugin)s ") % {"name": pyfile.name, "plugin": pyfile.pluginname}) + except Exception: + pass + + + def captchaTask(self, task): + if self.getConfig("captcha") and task.isTextual(): + task.handler.append(self) + task.setWaiting(60) + + page = getURL("http://www.freeimagehosting.net/upload.php", + post={"attached": (FORM_FILE, task.captchaFile)}, multipart=True) + + url = re.search(r"\[img\]([^\[]+)\[/img\]\[/url\]", page).group(1) + self.response(_("New Captcha Request: %s") % url) + self.response(_("Answer with 'c %s text on the captcha'") % task.id) + + + def run(self): + # connect to IRC etc. + self.sock = socket.socket() + host = self.getConfig("host") + self.sock.connect((host, self.getConfig("port"))) + + if self.getConfig("ssl"): + self.sock = ssl.wrap_socket(self.sock, cert_reqs=ssl.CERT_NONE) #@TODO: support custom certificate + + nick = self.getConfig("nick") + self.sock.send("NICK %s\r\n" % nick) + self.sock.send("USER %s %s bla :%s\r\n" % (nick, host, nick)) + for t in self.getConfig("owner").split(): + if t.strip().startswith("#"): + self.sock.send("JOIN %s\r\n" % t.strip()) + self.logInfo(_("Connected to"), host) + self.logInfo(_("Switching to listening mode!")) + try: + self.main_loop() + + except IRCError, ex: + self.sock.send("QUIT :byebye\r\n") + print_exc() + self.sock.close() + + + def main_loop(self): + readbuffer = "" + while True: + sleep(1) + fdset = select([self.sock], [], [], 0) + if self.sock not in fdset[0]: + continue + + if self.abort: + raise IRCError("quit") + + readbuffer += self.sock.recv(1024) + temp = readbuffer.split("\n") + readbuffer = temp.pop() + + for line in temp: + line = line.rstrip() + first = line.split() + + if first[0] == "PING": + self.sock.send("PONG %s\r\n" % first[1]) + + if first[0] == "ERROR": + raise IRCError(line) + + msg = line.split(None, 3) + if len(msg) < 4: + continue + + msg = { + "origin": msg[0][1:], + "action": msg[1], + "target": msg[2], + "text": msg[3][1:] + } + + self.handle_events(msg) + + + def handle_events(self, msg): + if not msg['origin'].split("!", 1)[0] in self.getConfig("owner").split(): + return + + if msg['target'].split("!", 1)[0] != self.getConfig("nick"): + return + + if msg['action'] != "PRIVMSG": + return + + # HANDLE CTCP ANTI FLOOD/BOT PROTECTION + if msg['text'] == "\x01VERSION\x01": + self.logDebug("Sending CTCP VERSION") + self.sock.send("NOTICE %s :%s\r\n" % (msg['origin'], "pyLoad! IRC Interface")) + return + elif msg['text'] == "\x01TIME\x01": + self.logDebug("Sending CTCP TIME") + self.sock.send("NOTICE %s :%d\r\n" % (msg['origin'], time.time())) + return + elif msg['text'] == "\x01LAG\x01": + self.logDebug("Received CTCP LAG") #: don't know how to answer + return + + trigger = "pass" + args = None + + try: + temp = msg['text'].split() + trigger = temp[0] + if len(temp) > 1: + args = temp[1:] + except Exception: + pass + + handler = getattr(self, "event_%s" % trigger, self.event_pass) + try: + res = handler(args) + for line in res: + self.response(line, msg['origin']) + except Exception, e: + self.logError(e) + + + def response(self, msg, origin=""): + if origin == "": + for t in self.getConfig("owner").split(): + self.sock.send("PRIVMSG %s :%s\r\n" % (t.strip(), msg)) + else: + self.sock.send("PRIVMSG %s :%s\r\n" % (origin.split("!", 1)[0], msg)) + + + #### Events + + def event_pass(self, args): + return [] + + + def event_status(self, args): + downloads = self.core.api.statusDownloads() + if not downloads: + return ["INFO: There are no active downloads currently."] + + temp_progress = "" + lines = ["ID - Name - Status - Speed - ETA - Progress"] + for data in downloads: + + if data.status == 5: + temp_progress = data.format_wait + else: + temp_progress = "%d%% (%s)" % (data.percent, data.format_size) + + lines.append("#%d - %s - %s - %s - %s - %s" % + ( + data.fid, + data.name, + data.statusmsg, + "%s/s" % formatSize(data.speed), + "%s" % data.format_eta, + temp_progress + )) + return lines + + + def event_queue(self, args): + ps = self.core.api.getQueueData() + + if not ps: + return ["INFO: There are no packages in queue."] + + lines = [] + for pack in ps: + lines.append('PACKAGE #%s: "%s" with %d links.' % (pack.pid, pack.name, len(pack.links))) + + return lines + + + def event_collector(self, args): + ps = self.core.api.getCollectorData() + if not ps: + return ["INFO: No packages in collector!"] + + lines = [] + for pack in ps: + lines.append('PACKAGE #%s: "%s" with %d links.' % (pack.pid, pack.name, len(pack.links))) + + return lines + + + def event_info(self, args): + if not args: + return ["ERROR: Use info like this: info <id>"] + + info = None + try: + info = self.core.api.getFileData(int(args[0])) + + except FileDoesNotExists: + return ["ERROR: Link doesn't exists."] + + return ['LINK #%s: %s (%s) [%s][%s]' % (info.fid, info.name, info.format_size, info.statusmsg, info.plugin)] + + + def event_packinfo(self, args): + if not args: + return ["ERROR: Use packinfo like this: packinfo <id>"] + + lines = [] + pack = None + try: + pack = self.core.api.getPackageData(int(args[0])) + + except PackageDoesNotExists: + return ["ERROR: Package doesn't exists."] + + id = args[0] + + self.more = [] + + lines.append('PACKAGE #%s: "%s" with %d links' % (id, pack.name, len(pack.links))) + for pyfile in pack.links: + self.more.append('LINK #%s: %s (%s) [%s][%s]' % (pyfile.fid, pyfile.name, pyfile.format_size, + pyfile.statusmsg, pyfile.plugin)) + + if len(self.more) < 6: + lines.extend(self.more) + self.more = [] + else: + lines.extend(self.more[:6]) + self.more = self.more[6:] + lines.append("%d more links do display." % len(self.more)) + + return lines + + + def event_more(self, args): + if not self.more: + return ["No more information to display."] + + lines = self.more[:6] + self.more = self.more[6:] + lines.append("%d more links do display." % len(self.more)) + + return lines + + + def event_start(self, args): + self.core.api.unpauseServer() + return ["INFO: Starting downloads."] + + + def event_stop(self, args): + self.core.api.pauseServer() + return ["INFO: No new downloads will be started."] + + + def event_add(self, args): + if len(args) < 2: + return ['ERROR: Add links like this: "add <packagename|id> links". ', + "This will add the link <link> to to the package <package> / the package with id <id>!"] + + pack = args[0].strip() + links = [x.strip() for x in args[1:]] + + count_added = 0 + count_failed = 0 + try: + id = int(pack) + pack = self.core.api.getPackageData(id) + if not pack: + return ["ERROR: Package doesn't exists."] + + #TODO add links + + return ["INFO: Added %d links to Package %s [#%d]" % (len(links), pack['name'], id)] + + except Exception: + # create new package + id = self.core.api.addPackage(pack, links, 1) + return ["INFO: Created new Package %s [#%d] with %d links." % (pack, id, len(links))] + + + def event_del(self, args): + if len(args) < 2: + return ["ERROR: Use del command like this: del -p|-l <id> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"] + + if args[0] == "-p": + ret = self.core.api.deletePackages(map(int, args[1:])) + return ["INFO: Deleted %d packages!" % len(args[1:])] + + elif args[0] == "-l": + ret = self.core.api.delLinks(map(int, args[1:])) + return ["INFO: Deleted %d links!" % len(args[1:])] + + else: + return ["ERROR: Use del command like this: del <-p|-l> <id> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"] + + + def event_push(self, args): + if not args: + return ["ERROR: Push package to queue like this: push <package id>"] + + id = int(args[0]) + try: + info = self.core.api.getPackageInfo(id) + except PackageDoesNotExists: + return ["ERROR: Package #%d does not exist." % id] + + self.core.api.pushToQueue(id) + return ["INFO: Pushed package #%d to queue." % id] + + + def event_pull(self, args): + if not args: + return ["ERROR: Pull package from queue like this: pull <package id>."] + + id = int(args[0]) + if not self.core.api.getPackageData(id): + return ["ERROR: Package #%d does not exist." % id] + + self.core.api.pullFromQueue(id) + return ["INFO: Pulled package #%d from queue to collector." % id] + + + def event_c(self, args): + """ captcha answer """ + if not args: + return ["ERROR: Captcha ID missing."] + + task = self.core.captchaManager.getTaskByID(args[0]) + if not task: + return ["ERROR: Captcha Task with ID %s does not exists." % args[0]] + + task.setResult(" ".join(args[1:])) + return ["INFO: Result %s saved." % " ".join(args[1:])] + + + def event_help(self, args): + lines = ["The following commands are available:", + "add <package|packid> <links> [...] Adds link to package. (creates new package if it does not exist)", + "queue Shows all packages in the queue", + "collector Shows all packages in collector", + "del -p|-l <id> [...] Deletes all packages|links with the ids specified", + "info <id> Shows info of the link with id <id>", + "packinfo <id> Shows info of the package with id <id>", + "more Shows more info when the result was truncated", + "start Starts all downloads", + "stop Stops the download (but not abort active downloads)", + "push <id> Push package to queue", + "pull <id> Pull package from queue", + "status Show general download status", + "help Shows this help message"] + return lines + + +class IRCError(Exception): + + def __init__(self, value): + self.value = value + + + def __str__(self): + return repr(self.value) diff --git a/module/plugins/addon/MergeFiles.py b/module/plugins/addon/MergeFiles.py new file mode 100644 index 000000000..18836f6ac --- /dev/null +++ b/module/plugins/addon/MergeFiles.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import os +import re + +from traceback import print_exc + +from pyload.plugin.Addon import Addon, threaded +from pyload.utils import safe_join, fs_encode + + +class MergeFiles(Addon): + __name__ = "MergeFiles" + __type__ = "addon" + __version__ = "0.13" + + __config__ = [("activated", "bool", "Activated", True)] + + __description__ = """Merges parts splitted with hjsplit""" + __license__ = "GPLv3" + __authors__ = [("and9000", "me@has-no-mail.com")] + + + BUFFER_SIZE = 4096 + + + def setup(self): + pass + + + @threaded + def packageFinished(self, pack): + files = {} + fid_dict = {} + for fid, data in pack.getChildren().iteritems(): + if re.search("\.\d{3}$", data['name']): + if data['name'][:-4] not in files: + files[data['name'][:-4]] = [] + files[data['name'][:-4]].append(data['name']) + files[data['name'][:-4]].sort() + fid_dict[data['name']] = fid + + download_folder = self.config['general']['download_folder'] + + if self.config['general']['folder_per_package']: + download_folder = safe_join(download_folder, pack.folder) + + for name, file_list in files.iteritems(): + self.logInfo(_("Starting merging of"), name) + + final_file = open(safe_join(download_folder, name), "wb") + for splitted_file in file_list: + self.logDebug("Merging part", splitted_file) + + pyfile = self.core.files.getFile(fid_dict[splitted_file]) + + pyfile.setStatus("processing") + + try: + with open(os.path.join(download_folder, splitted_file), "rb") as s_file: + size_written = 0 + s_file_size = int(os.path.getsize(os.path.join(download_folder, splitted_file))) + + while True: + f_buffer = s_file.read(self.BUFFER_SIZE) + if f_buffer: + final_file.write(f_buffer) + size_written += self.BUFFER_SIZE + pyfile.setProgress((size_written * 100) / s_file_size) + else: + break + + self.logDebug("Finished merging part", splitted_file) + + except Exception, e: + print_exc() + + finally: + pyfile.setProgress(100) + pyfile.setStatus("finished") + pyfile.release() + + self.logInfo(_("Finished merging of"), name) diff --git a/module/plugins/addon/MultiHome.py b/module/plugins/addon/MultiHome.py new file mode 100644 index 000000000..84b1e6ab7 --- /dev/null +++ b/module/plugins/addon/MultiHome.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- + +from time import time + +from pyload.plugin.Addon import Addon + + +class MultiHome(Addon): + __name__ = "MultiHome" + __type__ = "addon" + __version__ = "0.12" + + __config__ = [("interfaces", "str", "Interfaces", "None")] + + __description__ = """Ip address changer""" + __license__ = "GPLv3" + __authors__ = [("mkaay", "mkaay@mkaay.de")] + + + def setup(self): + self.register = {} + self.interfaces = [] + self.parseInterfaces(self.getConfig("interfaces").split(";")) + if not self.interfaces: + self.parseInterfaces([self.config['download']['interface']]) + self.setConfig("interfaces", self.toConfig()) + + + def toConfig(self): + return ";".join([i.adress for i in self.interfaces]) + + + def parseInterfaces(self, interfaces): + for interface in interfaces: + if not interface or str(interface).lower() == "none": + continue + self.interfaces.append(Interface(interface)) + + + def activate(self): + requestFactory = self.core.requestFactory + oldGetRequest = requestFactory.getRequest + + def getRequest(pluginName, account=None): + iface = self.bestInterface(pluginName, account) + if iface: + iface.useFor(pluginName, account) + requestFactory.iface = lambda: iface.adress + self.logDebug("Using address", iface.adress) + return oldGetRequest(pluginName, account) + + requestFactory.getRequest = getRequest + + + def bestInterface(self, pluginName, account): + best = None + for interface in self.interfaces: + if not best or interface.lastPluginAccess(pluginName, account) < best.lastPluginAccess(pluginName, account): + best = interface + return best + + +class Interface(object): + + def __init__(self, adress): + self.adress = adress + self.history = {} + + + def lastPluginAccess(self, pluginName, account): + if (pluginName, account) in self.history: + return self.history[(pluginName, account)] + return 0 + + + def useFor(self, pluginName, account): + self.history[(pluginName, account)] = time() + + + def __repr__(self): + return "<Interface - %s>" % self.adress diff --git a/module/plugins/addon/RestartFailed.py b/module/plugins/addon/RestartFailed.py new file mode 100644 index 000000000..2fe5f13bf --- /dev/null +++ b/module/plugins/addon/RestartFailed.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Addon import Addon + + +class RestartFailed(Addon): + __name__ = "RestartFailed" + __type__ = "addon" + __version__ = "1.57" + + __config__ = [("activated", "bool", "Activated" , True), + ("interval" , "int" , "Check interval in minutes", 90 )] + + __description__ = """Periodically restart all failed downloads in queue""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + # event_list = ["pluginConfigChanged"] + + MIN_INTERVAL = 15 * 60 #: 15m minimum check interval (value is in seconds) + + + def pluginConfigChanged(self, plugin, name, value): + if name == "interval": + interval = value * 60 + if self.MIN_INTERVAL <= interval != self.interval: + self.core.scheduler.removeJob(self.cb) + self.interval = interval + self.initPeriodical() + else: + self.logDebug("Invalid interval value, kept current") + + + def periodical(self): + self.logDebug(_("Restart failed downloads")) + self.core.api.restartFailed() + + + def setup(self): + self.interval = 0 + + + def activate(self): + self.pluginConfigChanged(self.__name__, "interval", self.getConfig("interval")) diff --git a/module/plugins/addon/RestartSlow.py b/module/plugins/addon/RestartSlow.py new file mode 100644 index 000000000..8621ed80d --- /dev/null +++ b/module/plugins/addon/RestartSlow.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +import pycurl + +from pyload.plugin.Addon import Addon + + +class RestartSlow(Addon): + __name__ = "RestartSlow" + __type__ = "addon" + __version__ = "0.02" + + __config__ = [("free_limit" , "int" , "Transfer speed threshold in kilobytes" , 100 ), + ("free_time" , "int" , "Sample interval in minutes" , 5 ), + ("premium_limit", "int" , "Transfer speed threshold for premium download in kilobytes", 300 ), + ("premium_time" , "int" , "Sample interval for premium download in minutes" , 2 ), + ("safe_mode" , "bool", "Don't restart if download is not resumable" , True)] + + __description__ = """Restart slow downloads""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + event_map = {'download-start': "downloadStarts"} + + + def setup(self): + self.info = {'chunk': {}} + + + def periodical(self): + if not self.pyfile.req.dl: + return + + if self.getConfig("safe_mode") and not self.pyfile.plugin.resumeDownload: + time = 30 + limit = 5 + else: + type = "premium" if self.pyfile.plugin.premium else "free" + time = max(30, self.getConfig("%s_time" % type) * 60) + limit = max(5, self.getConfig("%s_limit" % type) * 1024) + + chunks = [chunk for chunk in self.pyfile.req.dl.chunks \ + if chunk.id not in self.info['chunk'] or self.info['chunk'][chunk.id] not is (time, limit)] + + for chunk in chunks: + chunk.c.setopt(pycurl.LOW_SPEED_TIME , time) + chunk.c.setopt(pycurl.LOW_SPEED_LIMIT, limit) + + self.info['chunk'][chunk.id] = (time, limit) + + + def downloadStarts(self, pyfile, url, filename): + if self.cb or (self.getConfig("safe_mode") and not pyfile.plugin.resumeDownload): + return + + self.initPeriodical() diff --git a/module/plugins/addon/SkipRev.py b/module/plugins/addon/SkipRev.py new file mode 100644 index 000000000..d6908614d --- /dev/null +++ b/module/plugins/addon/SkipRev.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- + +from urllib import unquote +from urlparse import urlparse + +from pyload.plugin.Addon import Addon +from pyload.plugin.Plugin import SkipDownload + + +class SkipRev(Adoon): + __name__ = "SkipRev" + __type__ = "addon" + __version__ = "0.15" + + __config__ = [("tokeep", "int", "Number of rev files to keep for package (-1 to auto)", -1)] + + __description__ = """Skip files ending with extension rev""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + def _setup(self): + super(self.pyfile.plugin, self).setup() + if self.pyfile.hasStatus("skipped"): + raise SkipDownload(self.pyfile.getStatusName() or self.pyfile.pluginname) + + + def pyname(self, pyfile): + url = pyfile.url + plugin = pyfile.plugin + + if hasattr(plugin, "info") and 'name' in plugin.info and plugin.info['name']: + name = plugin.info['name'] + + elif hasattr(plugin, "parseInfos"): + name = next(plugin.parseInfos([url]))['name'] + + elif hasattr(plugin, "getInfo"): #@NOTE: if parseInfos was not found, getInfo should be missing too + name = plugin.getInfo(url)['name'] + + else: + self.logWarning("Unable to grab file name") + name = urlparse(unquote(url)).path.split('/')[-1]) + + return name + + + def downloadPreparing(self, pyfile): + if pyfile.getStatusName() is "unskipped" or not pyname(pyfile).endswith(".rev"): + return + + tokeep = self.getConfig("tokeep") + + if tokeep: + saved = [True for link in pyfile.package().getChildren() \ + if link.name.endswith(".rev") and (link.hasStatus("finished") or link.hasStatus("downloading"))].count(True) + + if not saved or saved < tokeep: #: keep one rev at least in auto mode + return + + pyfile.setCustomStatus("SkipRev", "skipped") + pyfile.plugin.setup = _setup #: work-around: inject status checker inside the preprocessing routine of the plugin + + + def downloadFailed(self, pyfile): + tokeep = self.getConfig("tokeep") + + if not tokeep: + return + + for link in pyfile.package().getChildren(): + if link.hasStatus("skipped") and link.name.endswith(".rev"): + if tokeep > -1 or pyfile.name.endswith(".rev"): + link.setStatus("queued") + else: + link.setCustomStatus("unskipped", "queued") + return diff --git a/module/plugins/addon/UnSkipOnFail.py b/module/plugins/addon/UnSkipOnFail.py new file mode 100644 index 000000000..bdbd4a5df --- /dev/null +++ b/module/plugins/addon/UnSkipOnFail.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- + +from os.path import basename + +from pyload.datatype.File import PyFile +from pyload.plugin.Addon import Addon +from pyload.utils import fs_encode + + +class UnSkipOnFail(Addon): + __name__ = "UnSkipOnFail" + __type__ = "addon" + __version__ = "0.02" + + __config__ = [("activated", "bool", "Activated", True)] + + __description__ = """When a download fails, restart skipped duplicates""" + __license__ = "GPLv3" + __authors__ = [("hagg", "")] + + + def downloadFailed(self, pyfile): + pyfile_name = basename(pyfile.name) + pid = pyfile.package().id + msg = _('look for skipped duplicates for %s (pid:%s)') + self.logInfo(msg % (pyfile_name, pid)) + dups = self.findDuplicates(pyfile) + for link in dups: + # check if link is "skipped"(=4) + if link.status == 4: + lpid = link.packageID + self.logInfo(_('restart "%s" (pid:%s)') % (pyfile_name, lpid)) + self.setLinkStatus(link, "queued") + + + def findDuplicates(self, pyfile): + """ Search all packages for duplicate links to "pyfile". + Duplicates are links that would overwrite "pyfile". + To test on duplicity the package-folder and link-name + of twolinks are compared (basename(link.name)). + So this method returns a list of all links with equal + package-folders and filenames as "pyfile", but except + the data for "pyfile" iotselöf. + It does MOT check the link's status. + """ + dups = [] + pyfile_name = fs_encode(basename(pyfile.name)) + # get packages (w/o files, as most file data is useless here) + queue = self.core.api.getQueue() + for package in queue: + # check if package-folder equals pyfile's package folder + if fs_encode(package.folder) == fs_encode(pyfile.package().folder): + # now get packaged data w/ files/links + pdata = self.core.api.getPackageData(package.pid) + if pdata.links: + for link in pdata.links: + link_name = fs_encode(basename(link.name)) + # check if link name collides with pdata's name + if link_name == pyfile_name: + # at last check if it is not pyfile itself + if link.fid != pyfile.id: + dups.append(link) + return dups + + + def setLinkStatus(self, link, new_status): + """ Change status of "link" to "new_status". + "link" has to be a valid FileData object, + "new_status" has to be a valid status name + (i.e. "queued" for this Plugin) + It creates a temporary PyFile object using + "link" data, changes its status, and tells + the core.files-manager to save its data. + """ + pyfile = PyFile(self.core.files, + link.fid, + link.url, + link.name, + link.size, + link.status, + link.error, + link.plugin, + link.packageID, + link.order) + pyfile.setStatus(new_status) + self.core.files.save() + pyfile.release() diff --git a/module/plugins/addon/UpdateManager.py b/module/plugins/addon/UpdateManager.py new file mode 100644 index 000000000..e31612c23 --- /dev/null +++ b/module/plugins/addon/UpdateManager.py @@ -0,0 +1,305 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import re +import sys + +from operator import itemgetter +from os import path, remove, stat + +from pyload.network.RequestFactory import getURL +from pyload.plugin.Addon import Expose, Addon, threaded +from pyload.utils import safe_join + + +class UpdateManager(Addon): + __name__ = "UpdateManager" + __type__ = "addon" + __version__ = "0.42" + + __config__ = [("activated" , "bool" , "Activated" , True ), + ("mode" , "pyLoad + plugins;plugins only", "Check updates for" , "pyLoad + plugins"), + ("interval" , "int" , "Check interval in hours" , 8 ), + ("autorestart" , "bool" , "Automatically restart pyLoad when required" , True ), + ("reloadplugins", "bool" , "Monitor plugins for code changes in debug mode", True ), + ("nodebugupdate", "bool" , "Don't check for updates in debug mode" , True )] + + __description__ = """Check for updates""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + # event_list = ["pluginConfigChanged"] + + SERVER_URL = "http://updatemanager.pyload.org" + VERSION = re.compile(r'__version__.*=.*("|\')([\d.]+)') + MIN_INTERVAL = 3 * 60 * 60 #: 3h minimum check interval (value is in seconds) + + + def pluginConfigChanged(self, plugin, name, value): + if name == "interval": + interval = value * 60 * 60 + if self.MIN_INTERVAL <= interval != self.interval: + self.core.scheduler.removeJob(self.cb) + self.interval = interval + self.initPeriodical() + else: + self.logDebug("Invalid interval value, kept current") + + elif name == "reloadplugins": + if self.cb2: + self.core.scheduler.removeJob(self.cb2) + if value is True and self.core.debug: + self.periodical2() + + + def activate(self): + self.pluginConfigChanged(self.__name__, "interval", self.getConfig("interval")) + x = lambda: self.pluginConfigChanged(self.__name__, "reloadplugins", self.getConfig("reloadplugins")) + self.core.scheduler.addJob(10, x, threaded=False) + + + def deactivate(self): + self.pluginConfigChanged(self.__name__, "reloadplugins", False) + + + def setup(self): + self.cb2 = None + self.interval = 0 + self.updating = False + self.info = {'pyload': False, 'version': None, 'plugins': False} + self.mtimes = {} #: store modification time for each plugin + + + def periodical2(self): + if not self.updating: + self.autoreloadPlugins() + + self.cb2 = self.core.scheduler.addJob(4, self.periodical2, threaded=False) + + + @Expose + def autoreloadPlugins(self): + """ reload and reindex all modified plugins """ + modules = filter( + lambda m: m and (m.__name__.startswith("pyload.plugin.") or + m.__name__.startswith("userplugins.")) and + m.__name__.count(".") >= 2, sys.modules.itervalues() + ) + + reloads = [] + + for m in modules: + root, type, name = m.__name__.rsplit(".", 2) + id = (type, name) + if type in self.core.pluginManager.plugins: + f = m.__file__.replace(".pyc", ".py") + if not path.isfile(f): + continue + + mtime = stat(f).st_mtime + + if id not in self.mtimes: + self.mtimes[id] = mtime + elif self.mtimes[id] < mtime: + reloads.append(id) + self.mtimes[id] = mtime + + return True if self.core.pluginManager.reloadPlugins(reloads) else False + + + def periodical(self): + if self.info['pyload'] or self.getConfig("nodebugupdate") and self.core.debug: + return + + self.updateThread() + + + def server_request(self): + try: + return getURL(self.SERVER_URL, get={'v': self.core.api.getServerVersion()}).splitlines() + except Exception: + self.logWarning(_("Unable to contact server to get updates")) + + + @threaded + def updateThread(self): + self.updating = True + + status = self.update(onlyplugin=self.getConfig("mode") == "plugins only") + + if status is 2 and self.getConfig("autorestart"): + self.core.api.restart() + else: + self.updating = False + + + @Expose + def updatePlugins(self): + """ simple wrapper for calling plugin update quickly """ + return self.update(onlyplugin=True) + + + @Expose + def update(self, onlyplugin=False): + """ check for updates """ + data = self.server_request() + + if not data: + exitcode = 0 + + elif data[0] == "None": + self.logInfo(_("No new pyLoad version available")) + updates = data[1:] + exitcode = self._updatePlugins(updates) + + elif onlyplugin: + exitcode = 0 + + else: + newversion = data[0] + self.logInfo(_("*** New pyLoad Version %s available ***") % newversion) + self.logInfo(_("*** Get it here: https://github.com/pyload/pyload/releases ***")) + exitcode = 3 + self.info['pyload'] = True + self.info['version'] = newversion + + return exitcode #: 0 = No plugins updated; 1 = Plugins updated; 2 = Plugins updated, but restart required; 3 = No plugins updated, new pyLoad version available + + + def _updatePlugins(self, updates): + """ check for plugin updates """ + + if self.info['plugins']: + return False #: plugins were already updated + + exitcode = 0 + updated = [] + + url = updates[0] + schema = updates[1].split('|') + + if "BLACKLIST" in updates: + blacklist = updates[updates.index('BLACKLIST') + 1:] + updates = updates[2:updates.index('BLACKLIST')] + else: + blacklist = None + updates = updates[2:] + + upgradable = [dict(zip(schema, x.split('|'))) for x in updates] + blacklisted = [(x.split('|')[0], x.split('|')[1].rsplit('.', 1)[0]) for x in blacklist] if blacklist else [] + + if blacklist: + # Protect internal plugins against removing + for i, t, n in enumerate(blacklisted): + if t == "internal": + blacklisted.pop(i) + continue + + for idx, plugin in enumerate(upgradable): + if n == plugin['name'] and t == plugin['type']: + upgradable.pop(idx) + break + + for t, n in self.removePlugins(sorted(blacklisted)): + self.logInfo(_("Removed blacklisted plugin [%(type)s] %(name)s") % { + 'type': t, + 'name': n, + }) + + for plugin in sorted(upgradable, key=itemgetter("type", "name")): + filename = plugin['name'] + type = plugin['type'] + version = plugin['version'] + + if filename.endswith(".pyc"): + name = filename[:filename.find("_")] + else: + name = filename.replace(".py", "") + + plugins = getattr(self.core.pluginManager, "%sPlugins" % type) + + oldver = float(plugins[name]['version']) if name in plugins else None + newver = float(version) + + if not oldver: + msg = "New plugin: [%(type)s] %(name)s (v%(newver).2f)" + elif newver > oldver: + msg = "New version of plugin: [%(type)s] %(name)s (v%(oldver).2f -> v%(newver).2f)" + else: + continue + + self.logInfo(_(msg) % {'type' : type, + 'name' : name, + 'oldver': oldver, + 'newver': newver}) + try: + content = getURL(url % plugin) + m = self.VERSION.search(content) + + if m and m.group(2) == version: + with open(safe_join("userplugins", prefix, filename), "wb") as f: + f.write(content) + + updated.append((prefix, name)) + else: + raise Exception, _("Version mismatch") + + except Exception, e: + self.logError(_("Error updating plugin: %s") % filename, str(e)) + + if updated: + reloaded = self.core.pluginManager.reloadPlugins(updated) + if reloaded: + self.logInfo(_("Plugins updated and reloaded")) + exitcode = 1 + else: + self.logInfo(_("*** Plugins have been updated, but need a pyLoad restart to be reloaded ***")) + self.info['plugins'] = True + exitcode = 2 + else: + self.logInfo(_("No plugin updates available")) + + return exitcode #: 0 = No plugins updated; 1 = Plugins updated; 2 = Plugins updated, but restart required + + + @Expose + def removePlugins(self, type_plugins): + """ delete plugins from disk """ + + if not type_plugins: + return + + self.logDebug("Requested deletion of plugins: %s" % type_plugins) + + removed = [] + + for type, name in type_plugins: + err = False + file = name + ".py" + + for root in ("userplugins", path.join(pypath, "pyload", "plugins")): + + filename = safe_join(root, type, file) + try: + remove(filename) + except Exception, e: + self.logDebug("Error deleting: %s" % path.basename(filename), e) + err = True + + filename += "c" + if path.isfile(filename): + try: + if type == "addon": + self.manager.deactivateAddon(name) + remove(filename) + except Exception, e: + self.logDebug("Error deleting: %s" % path.basename(filename), e) + err = True + + if not err: + id = (type, name) + removed.append(id) + + return removed #: return a list of the plugins successfully removed diff --git a/module/plugins/addon/WindowsPhoneToastNotify.py b/module/plugins/addon/WindowsPhoneToastNotify.py new file mode 100644 index 000000000..0ac6719e1 --- /dev/null +++ b/module/plugins/addon/WindowsPhoneToastNotify.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +import httplib +import time + +from pyload.plugin.Addon import Addon + + +class WindowsPhoneToastNotify(Addon): + __name__ = "WindowsPhoneToastNotify" + __type__ = "addon" + __version__ = "0.03" + + __config__ = [("force" , "bool", "Force even if client is connected" , False), + ("pushId" , "str" , "pushId" , "" ), + ("pushUrl" , "str" , "pushUrl" , "" ), + ("pushTimeout", "int" , "Timeout between notifications in seconds", 0 )] + + __description__ = """Send push notifications to Windows Phone""" + __license__ = "GPLv3" + __authors__ = [("Andy Voigt", "phone-support@hotmail.de")] + + + def getXmlData(self): + myxml = ("<?xml version='1.0' encoding='utf-8'?> <wp:Notification xmlns:wp='WPNotification'> " + "<wp:Toast> <wp:Text1>Pyload Mobile</wp:Text1> <wp:Text2>Captcha waiting!</wp:Text2> " + "</wp:Toast> </wp:Notification>") + return myxml + + + def doRequest(self): + URL = self.getConfig("pushUrl") + request = self.getXmlData() + webservice = httplib.HTTP(URL) + webservice.putrequest("POST", self.getConfig("pushId")) + webservice.putheader("Host", URL) + webservice.putheader("Content-type", "text/xml") + webservice.putheader("X-NotificationClass", "2") + webservice.putheader("X-WindowsPhone-Target", "toast") + webservice.putheader("Content-length", "%d" % len(request)) + webservice.endheaders() + webservice.send(request) + webservice.close() + self.setStorage("LAST_NOTIFY", time.time()) + + + def captchaTask(self, task): + if not self.getConfig("pushId") or not self.getConfig("pushUrl"): + return False + + if self.core.isClientConnected() and not self.getConfig("force"): + return False + + if (time.time() - float(self.getStorage("LAST_NOTIFY", 0))) < self.getConf("pushTimeout"): + return False + + self.doRequest() diff --git a/module/plugins/addon/XMPPInterface.py b/module/plugins/addon/XMPPInterface.py new file mode 100644 index 000000000..77a49af6f --- /dev/null +++ b/module/plugins/addon/XMPPInterface.py @@ -0,0 +1,252 @@ +# -*- coding: utf-8 -*- + +from pyxmpp import streamtls +from pyxmpp.all import JID, Message +from pyxmpp.interface import implements +from pyxmpp.interfaces import * +from pyxmpp.jabber.client import JabberClient + +from pyload.plugin.addon.IRCInterface import IRCInterface + + +class XMPPInterface(IRCInterface, JabberClient): + __name__ = "XMPPInterface" + __type__ = "addon" + __version__ = "0.11" + + __config__ = [("jid" , "str" , "Jabber ID" , "user@exmaple-jabber-server.org" ), + ("pw" , "str" , "Password" , "" ), + ("tls" , "bool", "Use TLS" , False ), + ("owners" , "str" , "List of JIDs accepting commands from", "me@icq-gateway.org;some@msn-gateway.org"), + ("info_file", "bool", "Inform about every file finished" , False ), + ("info_pack", "bool", "Inform about every package finished" , True ), + ("captcha" , "bool", "Send captcha requests" , True )] + + __description__ = """Connect to jabber and let owner perform different tasks""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org")] + + + implements(IMessageHandlersProvider) + + + def __init__(self, core, manager): + IRCInterface.__init__(self, core, manager) + + self.jid = JID(self.getConfig("jid")) + password = self.getConfig("pw") + + # if bare JID is provided add a resource -- it is required + if not self.jid.resource: + self.jid = JID(self.jid.node, self.jid.domain, "pyLoad") + + if self.getConfig("tls"): + tls_settings = streamtls.TLSSettings(require=True, verify_peer=False) + auth = ("sasl:PLAIN", "sasl:DIGEST-MD5") + else: + tls_settings = None + auth = ("sasl:DIGEST-MD5", "digest") + + # setup client with provided connection information + # and identity data + JabberClient.__init__(self, self.jid, password, + disco_name="pyLoad XMPP Client", disco_type="bot", + tls_settings=tls_settings, auth_methods=auth) + + self.interface_providers = [ + VersionHandler(self), + self, + ] + + + def activate(self): + self.new_package = {} + + self.start() + + + def packageFinished(self, pypack): + try: + if self.getConfig("info_pack"): + self.announce(_("Package finished: %s") % pypack.name) + except Exception: + pass + + + def downloadFinished(self, pyfile): + try: + if self.getConfig("info_file"): + self.announce( + _("Download finished: %(name)s @ %(plugin)s") % {"name": pyfile.name, "plugin": pyfile.pluginname}) + except Exception: + pass + + + def run(self): + # connect to IRC etc. + self.connect() + try: + self.loop() + except Exception, ex: + self.logError(ex) + + + def stream_state_changed(self, state, arg): + """This one is called when the state of stream connecting the component + to a server changes. This will usually be used to let the user + know what is going on.""" + self.logDebug("*** State changed: %s %r ***" % (state, arg)) + + + def disconnected(self): + self.logDebug("Client was disconnected") + + + def stream_closed(self, stream): + self.logDebug("Stream was closed", stream) + + + def stream_error(self, err): + self.logDebug("Stream Error", err) + + + def get_message_handlers(self): + """Return list of (message_type, message_handler) tuples. + + The handlers returned will be called when matching message is received + in a client session.""" + return [("normal", self.message)] + + + def message(self, stanza): + """Message handler for the component.""" + subject = stanza.get_subject() + body = stanza.get_body() + t = stanza.get_type() + self.logDebug("Message from %s received." % unicode(stanza.get_from())) + self.logDebug("Body: %s Subject: %s Type: %s" % (body, subject, t)) + + if t == "headline": + # 'headline' messages should never be replied to + return True + if subject: + subject = u"Re: " + subject + + to_jid = stanza.get_from() + from_jid = stanza.get_to() + + #j = JID() + to_name = to_jid.as_utf8() + from_name = from_jid.as_utf8() + + names = self.getConfig("owners").split(";") + + if to_name in names or to_jid.node + "@" + to_jid.domain in names: + messages = [] + + trigger = "pass" + args = None + + try: + temp = body.split() + trigger = temp[0] + if len(temp) > 1: + args = temp[1:] + except Exception: + pass + + handler = getattr(self, "event_%s" % trigger, self.event_pass) + try: + res = handler(args) + for line in res: + m = Message( + to_jid=to_jid, + from_jid=from_jid, + stanza_type=stanza.get_type(), + subject=subject, + body=line) + + messages.append(m) + except Exception, e: + self.logError(e) + + return messages + + else: + return True + + + def response(self, msg, origin=""): + return self.announce(msg) + + + def announce(self, message): + """ send message to all owners""" + for user in self.getConfig("owners").split(";"): + self.logDebug("Send message to", user) + + to_jid = JID(user) + + m = Message(from_jid=self.jid, + to_jid=to_jid, + stanza_type="chat", + body=message) + + stream = self.get_stream() + if not stream: + self.connect() + stream = self.get_stream() + + stream.send(m) + + + def beforeReconnecting(self, ip): + self.disconnect() + + + def afterReconnecting(self, ip): + self.connect() + + +class VersionHandler(object): + """Provides handler for a version query. + + This class will answer version query and announce 'jabber:iq:version' namespace + in the client's disco#info results.""" + + implements(IIqHandlersProvider, IFeaturesProvider) + + + def __init__(self, client): + """Just remember who created this.""" + self.client = client + + + def get_features(self): + """Return namespace which should the client include in its reply to a + disco#info query.""" + return ["jabber:iq:version"] + + + def get_iq_get_handlers(self): + """Return list of tuples (element_name, namespace, handler) describing + handlers of <iq type='get'/> stanzas""" + return [("query", "jabber:iq:version", self.get_version)] + + + def get_iq_set_handlers(self): + """Return empty list, as this class provides no <iq type='set'/> stanza handler.""" + return [] + + + def get_version(self, iq): + """Handler for jabber:iq:version queries. + + jabber:iq:version queries are not supported directly by PyXMPP, so the + XML node is accessed directly through the libxml2 API. This should be + used very carefully!""" + iq = iq.make_result_response() + q = iq.new_query("jabber:iq:version") + q.newTextChild(q.ns(), "name", "Echo component") + q.newTextChild(q.ns(), "version", "1.0") + return iq diff --git a/module/plugins/addon/__init__.py b/module/plugins/addon/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/module/plugins/addon/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/module/plugins/captcha/AdYouLike.py b/module/plugins/captcha/AdYouLike.py new file mode 100644 index 000000000..bfb8b5ccb --- /dev/null +++ b/module/plugins/captcha/AdYouLike.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Captcha import Captcha +from pyload.utils import json_loads + + +class AdYouLike(Captcha): + __name__ = "AdYouLike" + __type__ = "captcha" + __version__ = "0.02" + + __description__ = """AdYouLike captcha service plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + AYL_PATTERN = r'Adyoulike\.create\s*\((.+?)\)' + CALLBACK_PATTERN = r'(Adyoulike\.g\._jsonp_\d+)' + + + def detect_key(self, html=None): + if not html: + if hasattr(self.plugin, "html") and self.plugin.html: + html = self.plugin.html + else: + errmsg = _("AdYouLike html not found") + self.plugin.fail(errmsg) + raise TypeError(errmsg) + + m = re.search(self.AYL_PATTERN, html) + n = re.search(self.CALLBACK_PATTERN, html) + if m and n: + self.key = (m.group(1).strip(), n.group(1).strip()) + self.plugin.logDebug("AdYouLike ayl|callback: %s | %s" % self.key) + return self.key #: key is the tuple(ayl, callback) + else: + self.plugin.logDebug("AdYouLike ayl or callback not found") + return None + + + def challenge(self, key=None): + if not key: + if self.detect_key(): + key = self.key + else: + errmsg = _("AdYouLike key not found") + self.plugin.fail(errmsg) + raise TypeError(errmsg) + + ayl, callback = key + + # {"adyoulike":{"key":"P~zQ~O0zV0WTiAzC-iw0navWQpCLoYEP"}, + # "all":{"element_id":"ayl_private_cap_92300","lang":"fr","env":"prod"}} + ayl = json_loads(ayl) + + html = self.plugin.req.load("http://api-ayl.appspot.com/challenge", + get={'key' : ayl['adyoulike']['key'], + 'env' : ayl['all']['env'], + 'callback': callback}) + try: + challenge = json_loads(re.search(callback + r'\s*\((.+?)\)', html).group(1)) + except Exception: + errmsg = _("AdYouLike challenge pattern not found") + self.plugin.error(errmsg) + raise ValueError(errmsg) + + self.plugin.logDebug("AdYouLike challenge: %s" % challenge) + + return self.result(ayl, challenge) + + + def result(self, server, challenge): + # Adyoulike.g._jsonp_5579316662423138 + # ({"translations":{"fr":{"instructions_visual":"Recopiez « Soonnight » ci-dessous :"}}, + # "site_under":true,"clickable":true,"pixels":{"VIDEO_050":[],"DISPLAY":[],"VIDEO_000":[],"VIDEO_100":[], + # "VIDEO_025":[],"VIDEO_075":[]},"medium_type":"image/adyoulike", + # "iframes":{"big":"<iframe src=\"http://www.soonnight.com/campagn.html\" scrolling=\"no\" + # height=\"250\" width=\"300\" frameborder=\"0\"></iframe>"},"shares":{},"id":256, + # "token":"e6QuI4aRSnbIZJg02IsV6cp4JQ9~MjA1","formats":{"small":{"y":300,"x":0,"w":300,"h":60}, + # "big":{"y":0,"x":0,"w":300,"h":250},"hover":{"y":440,"x":0,"w":300,"h":60}}, + # "tid":"SqwuAdxT1EZoi4B5q0T63LN2AkiCJBg5"}) + + if isinstance(server, basestring): + server = json_loads(server) + + if isinstance(challenge, basestring): + challenge = json_loads(challenge) + + try: + instructions_visual = challenge['translations'][server['all']['lang']]['instructions_visual'] + result = re.search(u'«(.+?)»', instructions_visual).group(1).strip() + except Exception: + errmsg = _("AdYouLike result not found") + self.plugin.error(errmsg) + raise ValueError(errmsg) + + result = {'_ayl_captcha_engine' : "adyoulike", + '_ayl_env' : server['all']['env'], + '_ayl_tid' : challenge['tid'], + '_ayl_token_challenge': challenge['token'], + '_ayl_response' : response} + + self.plugin.logDebug("AdYouLike result: %s" % result) + + return result diff --git a/module/plugins/captcha/AdsCaptcha.py b/module/plugins/captcha/AdsCaptcha.py new file mode 100644 index 000000000..5c01a422e --- /dev/null +++ b/module/plugins/captcha/AdsCaptcha.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- + +import re + +from random import random + +from pyload.plugin.Captcha import Captcha + + +class AdsCaptcha(Captcha): + __name__ = "AdsCaptcha" + __type__ = "captcha" + __version__ = "0.06" + + __description__ = """AdsCaptcha captcha service plugin""" + __license__ = "GPLv3" + __authors__ = [("pyLoad Team", "admin@pyload.org")] + + + CAPTCHAID_PATTERN = r'api\.adscaptcha\.com/Get\.aspx\?[^"\']*CaptchaId=(\d+)' + PUBLICKEY_PATTERN = r'api\.adscaptcha\.com/Get\.aspx\?[^"\']*PublicKey=([\w-]+)' + + + def detect_key(self, html=None): + if not html: + if hasattr(self.plugin, "html") and self.plugin.html: + html = self.plugin.html + else: + errmsg = _("AdsCaptcha html not found") + self.plugin.fail(errmsg) + raise TypeError(errmsg) + + m = re.search(self.PUBLICKEY_PATTERN, html) + n = re.search(self.CAPTCHAID_PATTERN, html) + if m and n: + self.key = (m.group(1).strip(), n.group(1).strip()) #: key is the tuple(PublicKey, CaptchaId) + self.plugin.logDebug("AdsCaptcha key|id: %s | %s" % self.key) + return self.key + else: + self.plugin.logDebug("AdsCaptcha key or id not found") + return None + + + def challenge(self, key=None): + if not key: + if self.detect_key(): + key = self.key + else: + errmsg = _("AdsCaptcha key not found") + self.plugin.fail(errmsg) + raise TypeError(errmsg) + + PublicKey, CaptchaId = key + + html = self.plugin.req.load("http://api.adscaptcha.com/Get.aspx", get={'CaptchaId': CaptchaId, 'PublicKey': PublicKey}) + try: + challenge = re.search("challenge: '(.+?)',", html).group(1) + server = re.search("server: '(.+?)',", html).group(1) + except Exception: + errmsg = _("AdsCaptcha challenge pattern not found") + self.plugin.error(errmsg) + raise ValueError(errmsg) + + self.plugin.logDebug("AdsCaptcha challenge: %s" % challenge) + + return challenge, self.result(server, challenge) + + + def result(self, server, challenge): + result = self.plugin.decryptCaptcha("%sChallenge.aspx" % server, + get={'cid': challenge, 'dummy': random()}, + cookies=True, + imgtype="jpg") + + self.plugin.logDebug("AdsCaptcha result: %s" % result) + + return result diff --git a/module/plugins/captcha/ReCaptcha.py b/module/plugins/captcha/ReCaptcha.py new file mode 100644 index 000000000..4b900c57b --- /dev/null +++ b/module/plugins/captcha/ReCaptcha.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Captcha import Captcha + + +class ReCaptcha(Captcha): + __name__ = "ReCaptcha" + __type__ = "captcha" + __version__ = "0.08" + + __description__ = """ReCaptcha captcha service plugin""" + __license__ = "GPLv3" + __authors__ = [("pyLoad Team", "admin@pyload.org")] + + + KEY_PATTERN = r'recaptcha(?:/api|\.net)/(?:challenge|noscript)\?k=([\w-]+)' + KEY_AJAX_PATTERN = r'Recaptcha\.create\s*\(\s*["\']([\w-]+)' + + + def detect_key(self, html=None): + if not html: + if hasattr(self.plugin, "html") and self.plugin.html: + html = self.plugin.html + else: + errmsg = _("ReCaptcha html not found") + self.plugin.fail(errmsg) + raise TypeError(errmsg) + + m = re.search(self.KEY_PATTERN, html) or re.search(self.KEY_AJAX_PATTERN, html) + if m: + self.key = m.group(1).strip() + self.plugin.logDebug("ReCaptcha key: %s" % self.key) + return self.key + else: + self.plugin.logDebug("ReCaptcha key not found") + return None + + + def challenge(self, key=None): + if not key: + if self.detect_key(): + key = self.key + else: + errmsg = _("ReCaptcha key not found") + self.plugin.fail(errmsg) + raise TypeError(errmsg) + + html = self.plugin.req.load("http://www.google.com/recaptcha/api/challenge", get={'k': key}) + try: + challenge = re.search("challenge : '(.+?)',", html).group(1) + server = re.search("server : '(.+?)',", html).group(1) + except Exception: + errmsg = _("ReCaptcha challenge pattern not found") + self.plugin.error(errmsg) + raise ValueError(errmsg) + + self.plugin.logDebug("ReCaptcha challenge: %s" % challenge) + + return challenge, self.result(server, challenge) + + + def result(self, server, challenge): + result = self.plugin.decryptCaptcha("%simage" % server, + get={'c': challenge}, + cookies=True, + forceUser=True, + imgtype="jpg") + + self.plugin.logDebug("ReCaptcha result: %s" % result) + + return result diff --git a/module/plugins/captcha/SolveMedia.py b/module/plugins/captcha/SolveMedia.py new file mode 100644 index 000000000..6e2a6c362 --- /dev/null +++ b/module/plugins/captcha/SolveMedia.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Captcha import Captcha + + +class SolveMedia(Captcha): + __name__ = "SolveMedia" + __type__ = "captcha" + __version__ = "0.06" + + __description__ = """SolveMedia captcha service plugin""" + __license__ = "GPLv3" + __authors__ = [("pyLoad Team", "admin@pyload.org")] + + + KEY_PATTERN = r'api\.solvemedia\.com/papi/challenge\.(?:no)?script\?k=(.+?)["\']' + + + def challenge(self, key=None): + if not key: + if self.detect_key(): + key = self.key + else: + errmsg = _("SolveMedia key not found") + self.plugin.fail(errmsg) + raise TypeError(errmsg) + + html = self.plugin.req.load("http://api.solvemedia.com/papi/challenge.noscript", get={'k': key}) + try: + challenge = re.search(r'<input type=hidden name="adcopy_challenge" id="adcopy_challenge" value="([^"]+)">', + html).group(1) + server = "http://api.solvemedia.com/papi/media" + except Exception: + errmsg = _("SolveMedia challenge pattern not found") + self.plugin.error(errmsg) + raise ValueError(errmsg) + + self.plugin.logDebug("SolveMedia challenge: %s" % challenge) + + return challenge, self.result(server, challenge) + + + def result(self, server, challenge): + result = self.plugin.decryptCaptcha(server, get={'c': challenge}, imgtype="gif") + + self.plugin.logDebug("SolveMedia result: %s" % result) + + return result diff --git a/module/plugins/captcha/__init__.py b/module/plugins/captcha/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/module/plugins/captcha/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/module/plugins/container/CCF.py b/module/plugins/container/CCF.py new file mode 100644 index 000000000..a27511358 --- /dev/null +++ b/module/plugins/container/CCF.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import re + +from os import makedirs +from os.path import exists +from urllib2 import build_opener + +from MultipartPostHandler import MultipartPostHandler + +from pyload.plugin.Container import Container +from pyload.utils import safe_join + + +class CCF(Container): + __name__ = "CCF" + __version__ = "0.20" + + __pattern__ = r'.+\.ccf' + + __description__ = """CCF container decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("Willnix", "Willnix@pyload.org")] + + + def decrypt(self, pyfile): + infile = pyfile.url.replace("\n", "") + + opener = build_opener(MultipartPostHandler) + params = {"src": "ccf", + "filename": "test.ccf", + "upload": open(infile, "rb")} + tempdlc_content = opener.open('http://service.jdownloader.net/dlcrypt/getDLC.php', params).read() + + download_folder = self.config['general']['download_folder'] + + tempdlc_name = safe_join(download_folder, "tmp_%s.dlc" % pyfile.name) + with open(tempdlc_name, "w") as tempdlc: + tempdlc.write(re.search(r'<dlc>(.*)</dlc>', tempdlc_content, re.S).group(1)) + + self.urls = [tempdlc_name] diff --git a/module/plugins/container/LinkList.py b/module/plugins/container/LinkList.py new file mode 100644 index 000000000..305da8a38 --- /dev/null +++ b/module/plugins/container/LinkList.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- + +import codecs + +from pyload.plugin.Container import Container +from pyload.utils import fs_encode + + +class LinkList(Container): + __name__ = "LinkList" + __version__ = "0.12" + + __pattern__ = r'.+\.txt' + __config__ = [("clear", "bool", "Clear Linklist after adding", False), + ("encoding", "string", "File encoding (default utf-8)", "")] + + __description__ = """Read link lists in txt format""" + __license__ = "GPLv3" + __authors__ = [("spoob", "spoob@pyload.org"), + ("jeix", "jeix@hasnomail.com")] + + + def decrypt(self, pyfile): + try: + file_enc = codecs.lookup(self.getConfig("encoding")).name + except Exception: + file_enc = "utf-8" + + file_name = fs_encode(pyfile.url) + + txt = codecs.open(file_name, 'r', file_enc) + links = txt.readlines() + curPack = "Parsed links from %s" % pyfile.name + + packages = {curPack:[],} + + for link in links: + link = link.strip() + if not link: + continue + + if link.startswith(";"): + continue + if link.startswith("[") and link.endswith("]"): + # new package + curPack = link[1:-1] + packages[curPack] = [] + continue + packages[curPack].append(link) + txt.close() + + # empty packages fix + + delete = [] + + for key,value in packages.iteritems(): + if not value: + delete.append(key) + + for key in delete: + del packages[key] + + if self.getConfig("clear"): + try: + txt = open(file_name, 'wb') + txt.close() + except Exception: + self.logWarning(_("LinkList could not be cleared")) + + for name, links in packages.iteritems(): + self.packages.append((name, links, name)) diff --git a/module/plugins/container/RSDF.py b/module/plugins/container/RSDF.py new file mode 100644 index 000000000..17f304f50 --- /dev/null +++ b/module/plugins/container/RSDF.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import base64 +import binascii +import re + +from pyload.plugin.Container import Container +from pyload.utils import fs_encode + + +class RSDF(Container): + __name__ = "RSDF" + __version__ = "0.24" + + __pattern__ = r'.+\.rsdf' + + __description__ = """RSDF container decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org"), + ("spoob", "spoob@pyload.org")] + + + def decrypt(self, pyfile): + + from Crypto.Cipher import AES + + infile = fs_encode(pyfile.url.replace("\n", "")) + Key = binascii.unhexlify('8C35192D964DC3182C6F84F3252239EB4A320D2500000000') + + IV = binascii.unhexlify('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF') + IV_Cipher = AES.new(Key, AES.MODE_ECB) + IV = IV_Cipher.encrypt(IV) + + obj = AES.new(Key, AES.MODE_CFB, IV) + + try: + with open(infile, 'r') as rsdf: + data = rsdf.read() + except IOError, e: + self.fail(str(e)) + + if re.search(r"<title>404 - Not Found", data) is None: + data = binascii.unhexlify(''.join(data.split())) + data = data.splitlines() + + for link in data: + if not link: + continue + link = base64.b64decode(link) + link = obj.decrypt(link) + decryptedUrl = link.replace('CCF: ', '') + self.urls.append(decryptedUrl) + + self.logDebug("Adding package %s with %d links" % (pyfile.package().name, len(self.urls))) diff --git a/module/plugins/container/__init__.py b/module/plugins/container/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/module/plugins/container/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/module/plugins/crypter/BitshareCom.py b/module/plugins/crypter/BitshareCom.py new file mode 100644 index 000000000..524307127 --- /dev/null +++ b/module/plugins/crypter/BitshareCom.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class BitshareCom(SimpleCrypter): + __name__ = "BitshareCom" + __type__ = "crypter" + __version__ = "0.03" + + __pattern__ = r'http://(?:www\.)?bitshare\.com/\?d=\w+' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """Bitshare.com folder decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("stickell", "l.stickell@yahoo.it")] + + + LINK_PATTERN = r'.+' + NAME_PATTERN = r'View public folder "(?P.+)"' diff --git a/module/plugins/crypter/C1NeonCom.py b/module/plugins/crypter/C1NeonCom.py new file mode 100644 index 000000000..a7973b041 --- /dev/null +++ b/module/plugins/crypter/C1NeonCom.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo + + +class C1neonCom(DeadCrypter): + __name__ = "C1neonCom" + __type__ = "crypter" + __version__ = "0.05" + + __pattern__ = r'http://(?:www\.)?c1neon\.com/.*?' + __config__ = [] + + __description__ = """C1neon.com decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("godofdream", "soilfiction@gmail.com")] + + +getInfo = create_getInfo(C1neonCom) diff --git a/module/plugins/crypter/ChipDe.py b/module/plugins/crypter/ChipDe.py new file mode 100644 index 000000000..3e00a7f66 --- /dev/null +++ b/module/plugins/crypter/ChipDe.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +import re +from pyload.plugin.Crypter import Crypter + + +class ChipDe(Crypter): + __name__ = "ChipDe" + __type__ = "crypter" + __version__ = "0.10" + + __pattern__ = r'http://(?:www\.)?chip\.de/video/.*\.html' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """Chip.de decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("4Christopher", "4Christopher@gmx.de")] + + + def decrypt(self, pyfile): + self.html = self.load(pyfile.url) + try: + f = re.search(r'"(http://video\.chip\.de/.+)"', self.html) + except Exception: + self.fail(_("Failed to find the URL")) + else: + self.urls = [f.group(1)] + self.logDebug("The file URL is %s" % self.urls[0]) diff --git a/module/plugins/crypter/CrockoCom.py b/module/plugins/crypter/CrockoCom.py new file mode 100644 index 000000000..577433510 --- /dev/null +++ b/module/plugins/crypter/CrockoCom.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class CrockoCom(SimpleCrypter): + __name__ = "CrockoCom" + __type__ = "crypter" + __version__ = "0.01" + + __pattern__ = r'http://(?:www\.)?crocko\.com/f/.*' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """Crocko.com folder decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + LINK_PATTERN = r'download' diff --git a/module/plugins/crypter/CryptItCom.py b/module/plugins/crypter/CryptItCom.py new file mode 100644 index 000000000..cb3347f55 --- /dev/null +++ b/module/plugins/crypter/CryptItCom.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo + + +class CryptItCom(DeadCrypter): + __name__ = "CryptItCom" + __type__ = "crypter" + __version__ = "0.11" + + __pattern__ = r'http://(?:www\.)?crypt-it\.com/(s|e|d|c)/\w+' + __config__ = [] + + __description__ = """Crypt-it.com decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("jeix", "jeix@hasnomail.de")] + + +getInfo = create_getInfo(CryptItCom) diff --git a/module/plugins/crypter/CzshareCom.py b/module/plugins/crypter/CzshareCom.py new file mode 100644 index 000000000..71847ab48 --- /dev/null +++ b/module/plugins/crypter/CzshareCom.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +import re +from pyload.plugin.Crypter import Crypter + + +class CzshareCom(Crypter): + __name__ = "CzshareCom" + __type__ = "crypter" + __version__ = "0.20" + + __pattern__ = r'http://(?:www\.)?(czshare|sdilej)\.(com|cz)/folders/.*' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """Czshare.com folder decrypter plugin, now Sdilej.cz""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + FOLDER_PATTERN = r'\s*\s*(.*?)
    ' + LINK_PATTERN = r'info' + + + def decrypt(self, pyfile): + html = self.load(pyfile.url) + + m = re.search(self.FOLDER_PATTERN, html, re.S) + if m is None: + self.error(_("FOLDER_PATTERN not found")) + + self.urls.extend(re.findall(self.LINK_PATTERN, m.group(1))) diff --git a/module/plugins/crypter/DDLMusicOrg.py b/module/plugins/crypter/DDLMusicOrg.py new file mode 100644 index 000000000..2b6738799 --- /dev/null +++ b/module/plugins/crypter/DDLMusicOrg.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +import re + +from time import sleep + +from pyload.plugin.Crypter import Crypter + + +class DDLMusicOrg(Crypter): + __name__ = "DDLMusicOrg" + __type__ = "crypter" + __version__ = "0.30" + + __pattern__ = r'http://(?:www\.)?ddl-music\.org/captcha/ddlm_cr\d\.php\?\d+\?\d+' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """Ddl-music.org decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("mkaay", "mkaay@mkaay.de")] + + + def setup(self): + self.multiDL = False + + + def decrypt(self, pyfile): + html = self.load(pyfile.url, cookies=True) + + if re.search(r"Wer dies nicht rechnen kann", html) is not None: + self.offline() + + math = re.search(r"(\d+) ([+-]) (\d+) =\s+", htmlwithlink) + if m: + self.urls = [m.group(1)] + else: + self.retry() diff --git a/module/plugins/crypter/DailymotionBatch.py b/module/plugins/crypter/DailymotionBatch.py new file mode 100644 index 000000000..a056c73a4 --- /dev/null +++ b/module/plugins/crypter/DailymotionBatch.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from pyload.utils import json_loads +from pyload.plugin.Crypter import Crypter +from pyload.utils import safe_join + + +class DailymotionBatch(Crypter): + __name__ = "DailymotionBatch" + __type__ = "crypter" + __version__ = "0.01" + + __pattern__ = r'https?://(?:www\.)?dailymotion\.com/((playlists/)?(?Pplaylist|user)/)?(?P[\w^_]+)(?(TYPE)|#)' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """Dailymotion.com channel & playlist decrypter""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + def api_response(self, ref, req=None): + url = urljoin("https://api.dailymotion.com/", ref) + page = self.load(url, get=req) + return json_loads(page) + + + def getPlaylistInfo(self, id): + ref = "playlist/" + id + req = {"fields": "name,owner.screenname"} + playlist = self.api_response(ref, req) + + if "error" in playlist: + return + + name = playlist['name'] + owner = playlist['owner.screenname'] + return name, owner + + + def _getPlaylists(self, user_id, page=1): + ref = "user/%s/playlists" % user_id + req = {"fields": "id", "page": page, "limit": 100} + user = self.api_response(ref, req) + + if "error" in user: + return + + for playlist in user['list']: + yield playlist['id'] + + if user['has_more']: + for item in self._getPlaylists(user_id, page + 1): + yield item + + + def getPlaylists(self, user_id): + return [(id,) + self.getPlaylistInfo(id) for id in self._getPlaylists(user_id)] + + + def _getVideos(self, id, page=1): + ref = "playlist/%s/videos" % id + req = {"fields": "url", "page": page, "limit": 100} + playlist = self.api_response(ref, req) + + if "error" in playlist: + return + + for video in playlist['list']: + yield video['url'] + + if playlist['has_more']: + for item in self._getVideos(id, page + 1): + yield item + + + def getVideos(self, playlist_id): + return list(self._getVideos(playlist_id))[::-1] + + + def decrypt(self, pyfile): + m = re.match(self.__pattern__, pyfile.url) + m_id = m.group("ID") + m_type = m.group("TYPE") + + if m_type == "playlist": + self.logDebug("Url recognized as Playlist") + p_info = self.getPlaylistInfo(m_id) + playlists = [(m_id,) + p_info] if p_info else None + else: + self.logDebug("Url recognized as Channel") + playlists = self.getPlaylists(m_id) + self.logDebug("%s playlist\s found on channel \"%s\"" % (len(playlists), m_id)) + + if not playlists: + self.fail(_("No playlist available")) + + for p_id, p_name, p_owner in playlists: + p_videos = self.getVideos(p_id) + p_folder = safe_join(self.config['general']['download_folder'], p_owner, p_name) + self.logDebug("%s video\s found on playlist \"%s\"" % (len(p_videos), p_name)) + self.packages.append((p_name, p_videos, p_folder)) #: folder is NOT recognized by pyload 0.4.9! diff --git a/module/plugins/crypter/DataHu.py b/module/plugins/crypter/DataHu.py new file mode 100644 index 000000000..f69d6ee3e --- /dev/null +++ b/module/plugins/crypter/DataHu.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class DataHu(SimpleCrypter): + __name__ = "DataHu" + __type__ = "crypter" + __version__ = "0.06" + + __pattern__ = r'http://(?:www\.)?data\.hu/dir/\w+' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """Data.hu folder decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("crash", ""), + ("stickell", "l.stickell@yahoo.it")] + + + LINK_PATTERN = r'\1' + NAME_PATTERN = ur'(?P<N>.+) Let\xf6lt\xe9se' + + + def prepare(self): + super(DataHu, self).prepare() + + if u'K\xe9rlek add meg a jelsz\xf3t' in self.html: # Password protected + password = self.getPassword() + if not password: + self.fail(_("Password required")) + + self.logDebug("The folder is password protected', 'Using password: " + password) + + self.html = self.load(self.pyfile.url, post={'mappa_pass': password}, decode=True) + + if u'Hib\xe1s jelsz\xf3' in self.html: # Wrong password + self.fail(_("Wrong password")) diff --git a/module/plugins/crypter/DdlstorageCom.py b/module/plugins/crypter/DdlstorageCom.py new file mode 100644 index 000000000..1d87e975b --- /dev/null +++ b/module/plugins/crypter/DdlstorageCom.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo + + +class DdlstorageCom(DeadCrypter): + __name__ = "DdlstorageCom" + __type__ = "crypter" + __version__ = "0.03" + + __pattern__ = r'https?://(?:www\.)?ddlstorage\.com/folder/\w+' + __config__ = [] + + __description__ = """DDLStorage.com folder decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("godofdream", "soilfiction@gmail.com"), + ("stickell", "l.stickell@yahoo.it")] + + +getInfo = create_getInfo(DdlstorageCom) diff --git a/module/plugins/crypter/DepositfilesCom.py b/module/plugins/crypter/DepositfilesCom.py new file mode 100644 index 000000000..24fa9134a --- /dev/null +++ b/module/plugins/crypter/DepositfilesCom.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class DepositfilesCom(SimpleCrypter): + __name__ = "DepositfilesCom" + __type__ = "crypter" + __version__ = "0.01" + + __pattern__ = r'http://(?:www\.)?depositfiles\.com/folders/\w+' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """Depositfiles.com folder decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + LINK_PATTERN = r'
    ]*>\s*' diff --git a/module/plugins/crypter/Dereferer.py b/module/plugins/crypter/Dereferer.py new file mode 100644 index 000000000..598cba8bd --- /dev/null +++ b/module/plugins/crypter/Dereferer.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +import re + +from urllib import unquote + +from pyload.plugin.Crypter import Crypter + + +class Dereferer(Crypter): + __name__ = "Dereferer" + __type__ = "crypter" + __version__ = "0.10" + + __pattern__ = r'https?://([^/]+)/.*?(?P(ht|f)tps?(://|%3A%2F%2F).*)' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """Crypter for dereferers""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + def decrypt(self, pyfile): + link = re.match(self.__pattern__, pyfile.url).group('url') + self.urls = [unquote(link).rstrip('+')] diff --git a/module/plugins/crypter/DevhostStFolder.py b/module/plugins/crypter/DevhostStFolder.py new file mode 100644 index 000000000..9c81879c6 --- /dev/null +++ b/module/plugins/crypter/DevhostStFolder.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://d-h.st/users/shine/?fld_id=37263#files + +import re + +from urlparse import urljoin + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class DevhostStFolder(SimpleCrypter): + __name__ = "DevhostStFolder" + __type__ = "crypter" + __version__ = "0.03" + + __pattern__ = r'http://(?:www\.)?d-h\.st/users/(?P\w+)(/\?fld_id=(?P\d+))?' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """d-h.st folder decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("zapp-brannigan", "fuerst.reinje@web.de"), + ("Walter Purcaro", "vuolter@gmail.com")] + + + LINK_PATTERN = r'(?:/> |;">)Back to \w+<)' + OFFLINE_PATTERN = r'"/cHP">test\.png<' + + + def getFileInfo(self): + if re.search(self.OFFLINE_PATTERN, self.html): + self.offline() + + try: + id = re.match(self.__pattern__, self.pyfile.url).group('ID') + if id == "0": + raise + + p = r'href="(.+?)">Back to \w+<' + m = re.search(p, self.html) + html = self.load(urljoin("http://d-h.st", m.group(1)), + cookies=False) + + p = '\?fld_id=%s.*?">(.+?)<' % id + m = re.search(p, html) + name = folder = m.group(1) + + except Exception, e: + self.logDebug(e) + name = folder = re.match(self.__pattern__, self.pyfile.url).group('USER') + + return {'name': name, 'folder': folder} + + + def getLinks(self): + return [urljoin("http://d-h.st", link) for link in re.findall(self.LINK_PATTERN, self.html)] diff --git a/module/plugins/crypter/DlProtectCom.py b/module/plugins/crypter/DlProtectCom.py new file mode 100644 index 000000000..539cdc21c --- /dev/null +++ b/module/plugins/crypter/DlProtectCom.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- + +import re + +from base64 import urlsafe_b64encode +from time import time + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class DlProtectCom(SimpleCrypter): + __name__ = "DlProtectCom" + __type__ = "crypter" + __version__ = "0.01" + + __pattern__ = r'http://(?:www\.)?dl-protect\.com/((en|fr)/)?(?P\w+)' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """Dl-protect.com decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + OFFLINE_PATTERN = r'>Unfortunately, the link you are looking for is not found' + + + def getLinks(self): + # Direct link with redirect + if not re.match(r"http://(?:www\.)?dl-protect\.com", self.req.http.lastEffectiveURL): + return [self.req.http.lastEffectiveURL] + + #id = re.match(self.__pattern__, self.pyfile.url).group("ID") + key = re.search(r'name="id_key" value="(.+?)"', self.html).group(1) + + post_req = {"id_key": key, "submitform": ""} + + if self.OFFLINE_PATTERN in self.html: + self.offline() + elif ">Please click on continue to see the content" in self.html: + post_req.update({"submitform": "Continue"}) + else: + mstime = int(round(time() * 1000)) + b64time = "_" + urlsafe_b64encode(str(mstime)).replace("=", "%3D") + + post_req.update({"i": b64time, "submitform": "Decrypt+link"}) + + if ">Password :" in self.html: + post_req['pwd'] = self.getPassword() + + if ">Security Code" in self.html: + captcha_id = re.search(r'/captcha\.php\?uid=(.+?)"', self.html).group(1) + captcha_url = "http://www.dl-protect.com/captcha.php?uid=" + captcha_id + captcha_code = self.decryptCaptcha(captcha_url, imgtype="gif") + + post_req['secure'] = captcha_code + + self.html = self.load(self.pyfile.url, post=post_req) + + for errmsg in (">The password is incorrect", ">The security code is incorrect"): + if errmsg in self.html: + self.fail(_(errmsg[1:])) + + pattern = r'' + return re.findall(pattern, self.html) diff --git a/module/plugins/crypter/DontKnowMe.py b/module/plugins/crypter/DontKnowMe.py new file mode 100644 index 000000000..c2b9649ed --- /dev/null +++ b/module/plugins/crypter/DontKnowMe.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +import re + +from urllib import unquote + +from pyload.plugin.Crypter import Crypter + + +class DontKnowMe(Crypter): + __name__ = "DontKnowMe" + __type__ = "crypter" + __version__ = "0.10" + + __pattern__ = r'http://(?:www\.)?dontknow\.me/at/\?.+$' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """DontKnow.me decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("selaux", "")] + + + LINK_PATTERN = r'http://dontknow\.me/at/\?(.+)$' + + + def decrypt(self, pyfile): + link = re.findall(self.LINK_PATTERN, pyfile.url)[0] + self.urls = [unquote(link)] diff --git a/module/plugins/crypter/DuckCryptInfo.py b/module/plugins/crypter/DuckCryptInfo.py new file mode 100644 index 000000000..55681fd5e --- /dev/null +++ b/module/plugins/crypter/DuckCryptInfo.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +import re + +from BeautifulSoup import BeautifulSoup + +from pyload.plugin.Crypter import Crypter + + +class DuckCryptInfo(Crypter): + __name__ = "DuckCryptInfo" + __type__ = "crypter" + __version__ = "0.02" + + __pattern__ = r'http://(?:www\.)?duckcrypt\.info/(folder|wait|link)/(\w+)/?(\w*)' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """DuckCrypt.info decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("godofdream", "soilfiction@gmail.com")] + + + TIMER_PATTERN = r'(.*)' + + + def decrypt(self, pyfile): + url = pyfile.url + + m = re.match(self.__pattern__, url) + if m is None: + self.fail(_("Weird error in link")) + if str(m.group(1)) == "link": + self.handleLink(url) + else: + self.handleFolder(m) + + + def handleFolder(self, m): + html = self.load("http://duckcrypt.info/ajax/auth.php?hash=" + str(m.group(2))) + m = re.match(self.__pattern__, html) + self.logDebug("Redirectet to " + str(m.group(0))) + html = self.load(str(m.group(0))) + soup = BeautifulSoup(html) + cryptlinks = soup.findAll("div", attrs={"class": "folderbox"}) + self.logDebug("Redirectet to " + str(cryptlinks)) + if not cryptlinks: + self.error(_("No link found")) + for clink in cryptlinks: + if clink.find("a"): + self.handleLink(clink.find("a")['href']) + + + def handleLink(self, url): + html = self.load(url) + soup = BeautifulSoup(html) + self.urls = [soup.find("iframe")['src']] + if not self.urls: + self.logInfo(_("No link found")) diff --git a/module/plugins/crypter/DuploadOrg.py b/module/plugins/crypter/DuploadOrg.py new file mode 100644 index 000000000..70745b550 --- /dev/null +++ b/module/plugins/crypter/DuploadOrg.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo + + +class DuploadOrg(DeadCrypter): + __name__ = "DuploadOrg" + __type__ = "crypter" + __version__ = "0.02" + + __pattern__ = r'http://(?:www\.)?dupload\.org/folder/\d+' + __config__ = [] + + __description__ = """Dupload.org folder decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("stickell", "l.stickell@yahoo.it")] + + +getInfo = create_getInfo(DuploadOrg) diff --git a/module/plugins/crypter/EasybytezCom.py b/module/plugins/crypter/EasybytezCom.py new file mode 100644 index 000000000..9a4fca74a --- /dev/null +++ b/module/plugins/crypter/EasybytezCom.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSCrypter import XFSCrypter + + +class EasybytezCom(XFSCrypter): + __name__ = "EasybytezCom" + __type__ = "crypter" + __version__ = "0.10" + + __pattern__ = r'http://(?:www\.)?easybytez\.com/users/\d+/\d+' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """Easybytez.com folder decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("stickell", "l.stickell@yahoo.it")] + + + HOSTER_DOMAIN = "easybytez.com" + + LOGIN_ACCOUNT = True diff --git a/module/plugins/crypter/EmbeduploadCom.py b/module/plugins/crypter/EmbeduploadCom.py new file mode 100644 index 000000000..3265a939e --- /dev/null +++ b/module/plugins/crypter/EmbeduploadCom.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +import re +from pyload.plugin.Crypter import Crypter +from pyload.network.HTTPRequest import BadHeader + + +class EmbeduploadCom(Crypter): + __name__ = "EmbeduploadCom" + __type__ = "crypter" + __version__ = "0.02" + + __pattern__ = r'http://(?:www\.)?embedupload\.com/\?d=.*' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True), + ("preferedHoster", "str", "Prefered hoster list (bar-separated)", "embedupload"), + ("ignoredHoster", "str", "Ignored hoster list (bar-separated)", "")] + + __description__ = """EmbedUpload.com decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + LINK_PATTERN = r'
    ]*>\s*' + + + def decrypt(self, pyfile): + self.html = self.load(pyfile.url, decode=True) + tmp_links = [] + + m = re.findall(self.LINK_PATTERN, self.html) + if m: + prefered_set = set(self.getConfig("preferedHoster").split('|')) + prefered_set = map(lambda s: s.lower().split('.')[0], prefered_set) + + self.logDebug("PF: %s" % prefered_set) + + tmp_links.extend([x[1] for x in m if x[0] in prefered_set]) + self.urls = self.getLocation(tmp_links) + + if not self.urls: + ignored_set = set(self.getConfig("ignoredHoster").split('|')) + ignored_set = map(lambda s: s.lower().split('.')[0], ignored_set) + + self.logDebug("IG: %s" % ignored_set) + + tmp_links.extend([x[1] for x in m if x[0] not in ignored_set]) + self.urls = self.getLocation(tmp_links) + + + def getLocation(self, tmp_links): + new_links = [] + for link in tmp_links: + try: + header = self.load(link, just_header=True) + if 'location' in header: + new_links.append(header['location']) + except BadHeader: + pass + return new_links diff --git a/module/plugins/crypter/FilebeerInfo.py b/module/plugins/crypter/FilebeerInfo.py new file mode 100644 index 000000000..a6faa5cab --- /dev/null +++ b/module/plugins/crypter/FilebeerInfo.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter, create_getInfo + + +class FilebeerInfo(DeadCrypter): + __name__ = "FilebeerInfo" + __type__ = "crypter" + __version__ = "0.02" + + __pattern__ = r'http://(?:www\.)?filebeer\.info/(\d+~f).*' + __config__ = [] + + __description__ = """Filebeer.info folder decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + +getInfo = create_getInfo(FilebeerInfo) diff --git a/module/plugins/crypter/FilecloudIo.py b/module/plugins/crypter/FilecloudIo.py new file mode 100644 index 000000000..f4c967a07 --- /dev/null +++ b/module/plugins/crypter/FilecloudIo.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class FilecloudIo(SimpleCrypter): + __name__ = "FilecloudIo" + __type__ = "crypter" + __version__ = "0.03" + + __pattern__ = r'https?://(?:www\.)?(filecloud\.io|ifile\.it)/_\w+' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + + __description__ = """Filecloud.io folder decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + LINK_PATTERN = r'href="(http://filecloud\.io/\w+)" title' + NAME_PATTERN = r'>(?P.+?) - filecloud\.io<' diff --git a/module/plugins/crypter/FilecryptCc.py b/module/plugins/crypter/FilecryptCc.py new file mode 100644 index 000000000..475c1503f --- /dev/null +++ b/module/plugins/crypter/FilecryptCc.py @@ -0,0 +1,148 @@ +# -*- coding: utf-8 -*- + +import base64 +import binascii +import re + +from Crypto.Cipher import AES + +from pyload.plugin.Crypter import Crypter + + +class FilecryptCc(Crypter): + __name__ = "FilecryptCc" + __type__ = "crypter" + __version__ = "0.05" + + __pattern__ = r'https?://(?:www\.)?filecrypt\.cc/Container/\w+' + + __description__ = """Filecrypt.cc decrypter plugin""" + __license__ = "GPLv3" + __authors__ = [("zapp-brannigan", "fuerst.reinje@web.de")] + + + # URL_REPLACEMENTS = [(r'.html$', ""), (r'$', ".html")] #@TODO: Extend SimpleCrypter + + DLC_LINK_PATTERN = r'