From 4513f03e8f2b8f2bd92c306fff70f7899a153bda Mon Sep 17 00:00:00 2001 From: Ruud Date: Tue, 11 Mar 2014 23:31:37 +0100 Subject: [PATCH] Move movie to single file --- couchpotato/core/media/_base/providers/base.py | 1 + couchpotato/core/media/movie/_base/__init__.py | 2 +- .../media/movie/providers/automation/bluray.py | 105 ++++++ .../movie/providers/automation/bluray/__init__.py | 32 -- .../movie/providers/automation/bluray/main.py | 75 ---- .../media/movie/providers/automation/flixster.py | 83 ++++ .../providers/automation/flixster/__init__.py | 35 -- .../movie/providers/automation/flixster/main.py | 50 --- .../media/movie/providers/automation/goodfilms.py | 81 ++++ .../providers/automation/goodfilms/__init__.py | 29 -- .../movie/providers/automation/goodfilms/main.py | 54 --- .../core/media/movie/providers/automation/imdb.py | 211 +++++++++++ .../movie/providers/automation/imdb/__init__.py | 70 ---- .../media/movie/providers/automation/imdb/main.py | 143 ------- .../media/movie/providers/automation/itunes.py | 95 +++++ .../movie/providers/automation/itunes/__init__.py | 37 -- .../movie/providers/automation/itunes/main.py | 60 --- .../media/movie/providers/automation/kinepolis.py | 52 +++ .../providers/automation/kinepolis/__init__.py | 25 -- .../movie/providers/automation/kinepolis/main.py | 29 -- .../media/movie/providers/automation/letterboxd.py | 84 +++++ .../providers/automation/letterboxd/__init__.py | 35 -- .../movie/providers/automation/letterboxd/main.py | 51 --- .../media/movie/providers/automation/moviemeter.py | 51 +++ .../providers/automation/moviemeter/__init__.py | 25 -- .../movie/providers/automation/moviemeter/main.py | 28 -- .../media/movie/providers/automation/movies_io.py | 72 ++++ .../providers/automation/movies_io/__init__.py | 35 -- .../movie/providers/automation/movies_io/main.py | 39 -- .../movie/providers/automation/rottentomatoes.py | 94 +++++ .../automation/rottentomatoes/__init__.py | 43 --- .../providers/automation/rottentomatoes/main.py | 53 --- .../core/media/movie/providers/automation/trakt.py | 81 ++++ .../movie/providers/automation/trakt/__init__.py | 39 -- .../media/movie/providers/automation/trakt/main.py | 44 --- .../core/media/movie/providers/info/_modifier.py | 119 ++++++ .../movie/providers/info/_modifier/__init__.py | 7 - .../media/movie/providers/info/_modifier/main.py | 117 ------ .../media/movie/providers/info/couchpotatoapi.py | 120 ++++++ .../providers/info/couchpotatoapi/__init__.py | 7 - .../movie/providers/info/couchpotatoapi/main.py | 118 ------ .../core/media/movie/providers/info/omdbapi.py | 129 +++++++ .../media/movie/providers/info/omdbapi/__init__.py | 7 - .../media/movie/providers/info/omdbapi/main.py | 127 ------- .../core/media/movie/providers/info/themoviedb.py | 199 ++++++++++ .../movie/providers/info/themoviedb/__init__.py | 25 -- .../media/movie/providers/info/themoviedb/main.py | 177 --------- .../core/media/movie/providers/metadata/wmc.py | 31 ++ .../media/movie/providers/metadata/wmc/__init__.py | 25 -- .../media/movie/providers/metadata/wmc/main.py | 8 - .../core/media/movie/providers/metadata/xbmc.py | 243 ++++++++++++ .../movie/providers/metadata/xbmc/__init__.py | 70 ---- .../media/movie/providers/metadata/xbmc/main.py | 174 --------- .../core/media/movie/providers/nzb/__init__.py | 14 - .../core/media/movie/providers/nzb/binsearch.py | 24 ++ .../movie/providers/nzb/binsearch/__init__.py | 7 - .../media/movie/providers/nzb/binsearch/main.py | 24 -- .../core/media/movie/providers/nzb/newznab.py | 18 + .../media/movie/providers/nzb/newznab/__init__.py | 7 - .../core/media/movie/providers/nzb/newznab/main.py | 18 - .../core/media/movie/providers/nzb/nzbclub.py | 25 ++ .../media/movie/providers/nzb/nzbclub/__init__.py | 7 - .../core/media/movie/providers/nzb/nzbclub/main.py | 25 -- .../core/media/movie/providers/nzb/nzbindex.py | 28 ++ .../media/movie/providers/nzb/nzbindex/__init__.py | 7 - .../media/movie/providers/nzb/nzbindex/main.py | 28 -- .../core/media/movie/providers/nzb/omgwtfnzbs.py | 9 + .../movie/providers/nzb/omgwtfnzbs/__init__.py | 7 - .../media/movie/providers/nzb/omgwtfnzbs/main.py | 9 - .../core/media/movie/providers/torrent/__init__.py | 14 - .../media/movie/providers/torrent/awesomehd.py | 11 + .../movie/providers/torrent/awesomehd/__init__.py | 8 - .../movie/providers/torrent/awesomehd/main.py | 9 - .../core/media/movie/providers/torrent/bithdtv.py | 19 + .../movie/providers/torrent/bithdtv/__init__.py | 7 - .../media/movie/providers/torrent/bithdtv/main.py | 17 - .../core/media/movie/providers/torrent/bitsoup.py | 28 ++ .../movie/providers/torrent/bitsoup/__init__.py | 7 - .../media/movie/providers/torrent/bitsoup/main.py | 26 -- .../core/media/movie/providers/torrent/hdbits.py | 11 + .../movie/providers/torrent/hdbits/__init__.py | 7 - .../media/movie/providers/torrent/hdbits/main.py | 9 - .../media/movie/providers/torrent/ilovetorrents.py | 11 + .../providers/torrent/ilovetorrents/__init__.py | 7 - .../movie/providers/torrent/ilovetorrents/main.py | 9 - .../media/movie/providers/torrent/iptorrents.py | 20 + .../movie/providers/torrent/iptorrents/__init__.py | 7 - .../movie/providers/torrent/iptorrents/main.py | 20 - .../movie/providers/torrent/kickasstorrents.py | 9 + .../providers/torrent/kickasstorrents/__init__.py | 7 - .../providers/torrent/kickasstorrents/main.py | 9 - .../movie/providers/torrent/passthepopcorn.py | 36 ++ .../providers/torrent/passthepopcorn/__init__.py | 7 - .../movie/providers/torrent/passthepopcorn/main.py | 36 -- .../core/media/movie/providers/torrent/publichd.py | 14 + .../movie/providers/torrent/publichd/__init__.py | 7 - .../media/movie/providers/torrent/publichd/main.py | 12 - .../media/movie/providers/torrent/sceneaccess.py | 32 ++ .../providers/torrent/sceneaccess/__init__.py | 7 - .../movie/providers/torrent/sceneaccess/main.py | 30 -- .../media/movie/providers/torrent/thepiratebay.py | 26 ++ .../providers/torrent/thepiratebay/__init__.py | 7 - .../movie/providers/torrent/thepiratebay/main.py | 24 -- .../media/movie/providers/torrent/torrentbytes.py | 11 + .../providers/torrent/torrentbytes/__init__.py | 7 - .../movie/providers/torrent/torrentbytes/main.py | 9 - .../media/movie/providers/torrent/torrentday.py | 20 + .../movie/providers/torrent/torrentday/__init__.py | 7 - .../movie/providers/torrent/torrentday/main.py | 18 - .../media/movie/providers/torrent/torrentleech.py | 27 ++ .../providers/torrent/torrentleech/__init__.py | 7 - .../movie/providers/torrent/torrentleech/main.py | 25 -- .../media/movie/providers/torrent/torrentpotato.py | 19 + .../providers/torrent/torrentpotato/__init__.py | 7 - .../movie/providers/torrent/torrentpotato/main.py | 17 - .../media/movie/providers/torrent/torrentshack.py | 36 ++ .../providers/torrent/torrentshack/__init__.py | 7 - .../movie/providers/torrent/torrentshack/main.py | 34 -- .../core/media/movie/providers/torrent/yify.py | 11 + .../media/movie/providers/torrent/yify/__init__.py | 7 - .../media/movie/providers/torrent/yify/main.py | 9 - .../media/movie/providers/trailer/hdtrailers.py | 123 ++++++ .../movie/providers/trailer/hdtrailers/__init__.py | 7 - .../movie/providers/trailer/hdtrailers/main.py | 121 ------ .../media/movie/providers/userscript/allocine.py | 34 ++ .../providers/userscript/allocine/__init__.py | 5 - .../movie/providers/userscript/allocine/main.py | 32 -- .../movie/providers/userscript/appletrailers.py | 24 ++ .../providers/userscript/appletrailers/__init__.py | 5 - .../providers/userscript/appletrailers/main.py | 22 -- .../media/movie/providers/userscript/criticker.py | 8 + .../providers/userscript/criticker/__init__.py | 5 - .../movie/providers/userscript/criticker/main.py | 6 - .../media/movie/providers/userscript/filmweb.py | 32 ++ .../movie/providers/userscript/filmweb/__init__.py | 5 - .../movie/providers/userscript/filmweb/main.py | 30 -- .../media/movie/providers/userscript/flickchart.py | 32 ++ .../providers/userscript/flickchart/__init__.py | 5 - .../movie/providers/userscript/flickchart/main.py | 30 -- .../core/media/movie/providers/userscript/imdb.py | 12 + .../movie/providers/userscript/imdb/__init__.py | 5 - .../media/movie/providers/userscript/imdb/main.py | 11 - .../media/movie/providers/userscript/letterboxd.py | 8 + .../providers/userscript/letterboxd/__init__.py | 5 - .../movie/providers/userscript/letterboxd/main.py | 6 - .../media/movie/providers/userscript/moviemeter.py | 8 + .../providers/userscript/moviemeter/__init__.py | 5 - .../movie/providers/userscript/moviemeter/main.py | 6 - .../media/movie/providers/userscript/moviesio.py | 8 + .../providers/userscript/moviesio/__init__.py | 5 - .../movie/providers/userscript/moviesio/main.py | 6 - .../media/movie/providers/userscript/reddit.py | 19 + .../movie/providers/userscript/reddit/__init__.py | 5 - .../movie/providers/userscript/reddit/main.py | 17 - .../movie/providers/userscript/rottentomatoes.py | 41 ++ .../userscript/rottentomatoes/__init__.py | 5 - .../providers/userscript/rottentomatoes/main.py | 39 -- .../media/movie/providers/userscript/sharethe.py | 8 + .../providers/userscript/sharethe/__init__.py | 5 - .../movie/providers/userscript/sharethe/main.py | 6 - .../core/media/movie/providers/userscript/tmdb.py | 18 + .../movie/providers/userscript/tmdb/__init__.py | 5 - .../media/movie/providers/userscript/tmdb/main.py | 16 - .../core/media/movie/providers/userscript/trakt.py | 9 + .../movie/providers/userscript/trakt/__init__.py | 5 - .../media/movie/providers/userscript/trakt/main.py | 7 - .../core/media/movie/providers/userscript/whiwa.py | 8 + .../movie/providers/userscript/whiwa/__init__.py | 5 - .../media/movie/providers/userscript/whiwa/main.py | 6 - .../media/movie/providers/userscript/youteather.py | 15 + .../providers/userscript/youteather/__init__.py | 5 - .../movie/providers/userscript/youteather/main.py | 12 - couchpotato/core/media/movie/searcher.py | 418 +++++++++++++++++++++ couchpotato/core/media/movie/searcher/__init__.py | 74 ---- couchpotato/core/media/movie/searcher/main.py | 349 ----------------- 175 files changed, 3122 insertions(+), 3377 deletions(-) create mode 100644 couchpotato/core/media/movie/providers/automation/bluray.py delete mode 100644 couchpotato/core/media/movie/providers/automation/bluray/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/automation/bluray/main.py create mode 100644 couchpotato/core/media/movie/providers/automation/flixster.py delete mode 100644 couchpotato/core/media/movie/providers/automation/flixster/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/automation/flixster/main.py create mode 100644 couchpotato/core/media/movie/providers/automation/goodfilms.py delete mode 100644 couchpotato/core/media/movie/providers/automation/goodfilms/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/automation/goodfilms/main.py create mode 100644 couchpotato/core/media/movie/providers/automation/imdb.py delete mode 100644 couchpotato/core/media/movie/providers/automation/imdb/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/automation/imdb/main.py create mode 100644 couchpotato/core/media/movie/providers/automation/itunes.py delete mode 100644 couchpotato/core/media/movie/providers/automation/itunes/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/automation/itunes/main.py create mode 100644 couchpotato/core/media/movie/providers/automation/kinepolis.py delete mode 100644 couchpotato/core/media/movie/providers/automation/kinepolis/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/automation/kinepolis/main.py create mode 100644 couchpotato/core/media/movie/providers/automation/letterboxd.py delete mode 100644 couchpotato/core/media/movie/providers/automation/letterboxd/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/automation/letterboxd/main.py create mode 100644 couchpotato/core/media/movie/providers/automation/moviemeter.py delete mode 100644 couchpotato/core/media/movie/providers/automation/moviemeter/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/automation/moviemeter/main.py create mode 100644 couchpotato/core/media/movie/providers/automation/movies_io.py delete mode 100644 couchpotato/core/media/movie/providers/automation/movies_io/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/automation/movies_io/main.py create mode 100644 couchpotato/core/media/movie/providers/automation/rottentomatoes.py delete mode 100644 couchpotato/core/media/movie/providers/automation/rottentomatoes/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/automation/rottentomatoes/main.py create mode 100644 couchpotato/core/media/movie/providers/automation/trakt.py delete mode 100644 couchpotato/core/media/movie/providers/automation/trakt/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/automation/trakt/main.py create mode 100644 couchpotato/core/media/movie/providers/info/_modifier.py delete mode 100644 couchpotato/core/media/movie/providers/info/_modifier/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/info/_modifier/main.py create mode 100644 couchpotato/core/media/movie/providers/info/couchpotatoapi.py delete mode 100644 couchpotato/core/media/movie/providers/info/couchpotatoapi/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/info/couchpotatoapi/main.py create mode 100644 couchpotato/core/media/movie/providers/info/omdbapi.py delete mode 100644 couchpotato/core/media/movie/providers/info/omdbapi/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/info/omdbapi/main.py create mode 100644 couchpotato/core/media/movie/providers/info/themoviedb.py delete mode 100644 couchpotato/core/media/movie/providers/info/themoviedb/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/info/themoviedb/main.py create mode 100644 couchpotato/core/media/movie/providers/metadata/wmc.py delete mode 100644 couchpotato/core/media/movie/providers/metadata/wmc/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/metadata/wmc/main.py create mode 100644 couchpotato/core/media/movie/providers/metadata/xbmc.py delete mode 100644 couchpotato/core/media/movie/providers/metadata/xbmc/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/metadata/xbmc/main.py create mode 100644 couchpotato/core/media/movie/providers/nzb/binsearch.py delete mode 100644 couchpotato/core/media/movie/providers/nzb/binsearch/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/nzb/binsearch/main.py create mode 100644 couchpotato/core/media/movie/providers/nzb/newznab.py delete mode 100644 couchpotato/core/media/movie/providers/nzb/newznab/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/nzb/newznab/main.py create mode 100644 couchpotato/core/media/movie/providers/nzb/nzbclub.py delete mode 100644 couchpotato/core/media/movie/providers/nzb/nzbclub/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/nzb/nzbclub/main.py create mode 100644 couchpotato/core/media/movie/providers/nzb/nzbindex.py delete mode 100644 couchpotato/core/media/movie/providers/nzb/nzbindex/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/nzb/nzbindex/main.py create mode 100644 couchpotato/core/media/movie/providers/nzb/omgwtfnzbs.py delete mode 100644 couchpotato/core/media/movie/providers/nzb/omgwtfnzbs/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/nzb/omgwtfnzbs/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/awesomehd.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/awesomehd/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/awesomehd/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/bithdtv.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/bithdtv/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/bithdtv/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/bitsoup.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/bitsoup/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/bitsoup/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/hdbits.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/hdbits/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/hdbits/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/ilovetorrents.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/ilovetorrents/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/ilovetorrents/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/iptorrents.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/iptorrents/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/iptorrents/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/kickasstorrents.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/kickasstorrents/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/kickasstorrents/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/passthepopcorn.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/passthepopcorn/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/passthepopcorn/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/publichd.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/publichd/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/publichd/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/sceneaccess.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/sceneaccess/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/sceneaccess/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/thepiratebay.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/thepiratebay/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/thepiratebay/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/torrentbytes.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/torrentbytes/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/torrentbytes/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/torrentday.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/torrentday/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/torrentday/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/torrentleech.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/torrentleech/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/torrentleech/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/torrentpotato.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/torrentpotato/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/torrentpotato/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/torrentshack.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/torrentshack/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/torrentshack/main.py create mode 100644 couchpotato/core/media/movie/providers/torrent/yify.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/yify/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/torrent/yify/main.py create mode 100644 couchpotato/core/media/movie/providers/trailer/hdtrailers.py delete mode 100644 couchpotato/core/media/movie/providers/trailer/hdtrailers/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/trailer/hdtrailers/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/allocine.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/allocine/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/allocine/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/appletrailers.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/appletrailers/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/appletrailers/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/criticker.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/criticker/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/criticker/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/filmweb.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/filmweb/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/filmweb/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/flickchart.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/flickchart/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/flickchart/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/imdb.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/imdb/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/imdb/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/letterboxd.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/letterboxd/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/letterboxd/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/moviemeter.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/moviemeter/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/moviemeter/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/moviesio.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/moviesio/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/moviesio/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/reddit.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/reddit/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/reddit/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/rottentomatoes.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/rottentomatoes/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/rottentomatoes/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/sharethe.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/sharethe/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/sharethe/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/tmdb.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/tmdb/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/tmdb/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/trakt.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/trakt/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/trakt/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/whiwa.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/whiwa/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/whiwa/main.py create mode 100644 couchpotato/core/media/movie/providers/userscript/youteather.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/youteather/__init__.py delete mode 100644 couchpotato/core/media/movie/providers/userscript/youteather/main.py create mode 100644 couchpotato/core/media/movie/searcher.py delete mode 100644 couchpotato/core/media/movie/searcher/__init__.py delete mode 100644 couchpotato/core/media/movie/searcher/main.py diff --git a/couchpotato/core/media/_base/providers/base.py b/couchpotato/core/media/_base/providers/base.py index 761aedc..95ab128 100644 --- a/couchpotato/core/media/_base/providers/base.py +++ b/couchpotato/core/media/_base/providers/base.py @@ -14,6 +14,7 @@ import xml.etree.ElementTree as XMLTree log = CPLog(__name__) + class MultiProvider(Plugin): def __init__(self): diff --git a/couchpotato/core/media/movie/_base/__init__.py b/couchpotato/core/media/movie/_base/__init__.py index 2221133..acec594 100644 --- a/couchpotato/core/media/movie/_base/__init__.py +++ b/couchpotato/core/media/movie/_base/__init__.py @@ -1,7 +1,7 @@ from .main import MovieBase -def start(): +def autoload(): return MovieBase() config = [] diff --git a/couchpotato/core/media/movie/providers/automation/bluray.py b/couchpotato/core/media/movie/providers/automation/bluray.py new file mode 100644 index 0000000..b411900 --- /dev/null +++ b/couchpotato/core/media/movie/providers/automation/bluray.py @@ -0,0 +1,105 @@ +from bs4 import BeautifulSoup +from couchpotato.core.helpers.rss import RSS +from couchpotato.core.helpers.variable import tryInt +from couchpotato.core.logger import CPLog +from couchpotato.core.media.movie.providers.automation.base import Automation + +log = CPLog(__name__) + +autoload = 'Bluray' + + +class Bluray(Automation, RSS): + + interval = 1800 + rss_url = 'http://www.blu-ray.com/rss/newreleasesfeed.xml' + backlog_url = 'http://www.blu-ray.com/movies/movies.php?show=newreleases&page=%s' + + def getIMDBids(self): + + movies = [] + + if self.conf('backlog'): + + page = 0 + while True: + page += 1 + + url = self.backlog_url % page + data = self.getHTMLData(url) + soup = BeautifulSoup(data) + + try: + # Stop if the release year is before the minimal year + page_year = soup.body.find_all('center')[3].table.tr.find_all('td', recursive = False)[3].h3.get_text().split(', ')[1] + if tryInt(page_year) < self.getMinimal('year'): + break + + for table in soup.body.find_all('center')[3].table.tr.find_all('td', recursive = False)[3].find_all('table')[1:20]: + name = table.h3.get_text().lower().split('blu-ray')[0].strip() + year = table.small.get_text().split('|')[1].strip() + + if not name.find('/') == -1: # make sure it is not a double movie release + continue + + if tryInt(year) < self.getMinimal('year'): + continue + + imdb = self.search(name, year) + + if imdb: + if self.isMinimalMovie(imdb): + movies.append(imdb['imdb']) + except: + log.debug('Error loading page: %s', page) + break + + self.conf('backlog', value = False) + + rss_movies = self.getRSSData(self.rss_url) + + for movie in rss_movies: + name = self.getTextElement(movie, 'title').lower().split('blu-ray')[0].strip('(').rstrip() + year = self.getTextElement(movie, 'description').split('|')[1].strip('(').strip() + + if not name.find('/') == -1: # make sure it is not a double movie release + continue + + if tryInt(year) < self.getMinimal('year'): + continue + + imdb = self.search(name, year) + + if imdb: + if self.isMinimalMovie(imdb): + movies.append(imdb['imdb']) + + return movies + + +config = [{ + 'name': 'bluray', + 'groups': [ + { + 'tab': 'automation', + 'list': 'automation_providers', + 'name': 'bluray_automation', + 'label': 'Blu-ray.com', + 'description': 'Imports movies from blu-ray.com.', + 'options': [ + { + 'name': 'automation_enabled', + 'default': False, + 'type': 'enabler', + }, + { + 'name': 'backlog', + 'advanced': True, + 'description': 'Parses the history until the minimum movie year is reached. (Will be disabled once it has completed)', + 'default': False, + 'type': 'bool', + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/providers/automation/bluray/__init__.py b/couchpotato/core/media/movie/providers/automation/bluray/__init__.py deleted file mode 100644 index 519a711..0000000 --- a/couchpotato/core/media/movie/providers/automation/bluray/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -from .main import Bluray - - -def start(): - return Bluray() - -config = [{ - 'name': 'bluray', - 'groups': [ - { - 'tab': 'automation', - 'list': 'automation_providers', - 'name': 'bluray_automation', - 'label': 'Blu-ray.com', - 'description': 'Imports movies from blu-ray.com.', - 'options': [ - { - 'name': 'automation_enabled', - 'default': False, - 'type': 'enabler', - }, - { - 'name': 'backlog', - 'advanced': True, - 'description': 'Parses the history until the minimum movie year is reached. (Will be disabled once it has completed)', - 'default': False, - 'type': 'bool', - }, - ], - }, - ], -}] diff --git a/couchpotato/core/media/movie/providers/automation/bluray/main.py b/couchpotato/core/media/movie/providers/automation/bluray/main.py deleted file mode 100644 index 2e8ebff..0000000 --- a/couchpotato/core/media/movie/providers/automation/bluray/main.py +++ /dev/null @@ -1,75 +0,0 @@ -from bs4 import BeautifulSoup -from couchpotato.core.helpers.rss import RSS -from couchpotato.core.helpers.variable import tryInt -from couchpotato.core.logger import CPLog -from couchpotato.core.media.movie.providers.automation.base import Automation - -log = CPLog(__name__) - - -class Bluray(Automation, RSS): - - interval = 1800 - rss_url = 'http://www.blu-ray.com/rss/newreleasesfeed.xml' - backlog_url = 'http://www.blu-ray.com/movies/movies.php?show=newreleases&page=%s' - - def getIMDBids(self): - - movies = [] - - if self.conf('backlog'): - - page = 0 - while True: - page += 1 - - url = self.backlog_url % page - data = self.getHTMLData(url) - soup = BeautifulSoup(data) - - try: - # Stop if the release year is before the minimal year - page_year = soup.body.find_all('center')[3].table.tr.find_all('td', recursive = False)[3].h3.get_text().split(', ')[1] - if tryInt(page_year) < self.getMinimal('year'): - break - - for table in soup.body.find_all('center')[3].table.tr.find_all('td', recursive = False)[3].find_all('table')[1:20]: - name = table.h3.get_text().lower().split('blu-ray')[0].strip() - year = table.small.get_text().split('|')[1].strip() - - if not name.find('/') == -1: # make sure it is not a double movie release - continue - - if tryInt(year) < self.getMinimal('year'): - continue - - imdb = self.search(name, year) - - if imdb: - if self.isMinimalMovie(imdb): - movies.append(imdb['imdb']) - except: - log.debug('Error loading page: %s', page) - break - - self.conf('backlog', value = False) - - rss_movies = self.getRSSData(self.rss_url) - - for movie in rss_movies: - name = self.getTextElement(movie, 'title').lower().split('blu-ray')[0].strip('(').rstrip() - year = self.getTextElement(movie, 'description').split('|')[1].strip('(').strip() - - if not name.find('/') == -1: # make sure it is not a double movie release - continue - - if tryInt(year) < self.getMinimal('year'): - continue - - imdb = self.search(name, year) - - if imdb: - if self.isMinimalMovie(imdb): - movies.append(imdb['imdb']) - - return movies diff --git a/couchpotato/core/media/movie/providers/automation/flixster.py b/couchpotato/core/media/movie/providers/automation/flixster.py new file mode 100644 index 0000000..53a06d7 --- /dev/null +++ b/couchpotato/core/media/movie/providers/automation/flixster.py @@ -0,0 +1,83 @@ +from couchpotato.core.helpers.variable import tryInt, splitString +from couchpotato.core.logger import CPLog +from couchpotato.core.media.movie.providers.automation.base import Automation + +log = CPLog(__name__) + +autoload = 'Flixster' + + +class Flixster(Automation): + + url = 'http://www.flixster.com/api/users/%s/movies/ratings?scoreTypes=wts' + + interval = 60 + + def getIMDBids(self): + + ids = splitString(self.conf('automation_ids')) + + if len(ids) == 0: + return [] + + movies = [] + + for movie in self.getWatchlist(): + imdb_id = self.search(movie.get('title'), movie.get('year'), imdb_only = True) + movies.append(imdb_id) + + return movies + + def getWatchlist(self): + + enablers = [tryInt(x) for x in splitString(self.conf('automation_ids_use'))] + ids = splitString(self.conf('automation_ids')) + + index = -1 + movies = [] + for user_id in ids: + + index += 1 + if not enablers[index]: + continue + + data = self.getJsonData(self.url % user_id, decode_from = 'iso-8859-1') + + for movie in data: + movies.append({ + 'title': movie['movie']['title'], + 'year': movie['movie']['year'] + }) + + return movies + + +config = [{ + 'name': 'flixster', + 'groups': [ + { + 'tab': 'automation', + 'list': 'watchlist_providers', + 'name': 'flixster_automation', + 'label': 'Flixster', + 'description': 'Import movies from any public Flixster watchlist', + 'options': [ + { + 'name': 'automation_enabled', + 'default': False, + 'type': 'enabler', + }, + { + 'name': 'automation_ids_use', + 'label': 'Use', + }, + { + 'name': 'automation_ids', + 'label': 'User ID', + 'type': 'combined', + 'combine': ['automation_ids_use', 'automation_ids'], + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/providers/automation/flixster/__init__.py b/couchpotato/core/media/movie/providers/automation/flixster/__init__.py deleted file mode 100644 index 71bd83c..0000000 --- a/couchpotato/core/media/movie/providers/automation/flixster/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -from .main import Flixster - - -def start(): - return Flixster() - -config = [{ - 'name': 'flixster', - 'groups': [ - { - 'tab': 'automation', - 'list': 'watchlist_providers', - 'name': 'flixster_automation', - 'label': 'Flixster', - 'description': 'Import movies from any public Flixster watchlist', - 'options': [ - { - 'name': 'automation_enabled', - 'default': False, - 'type': 'enabler', - }, - { - 'name': 'automation_ids_use', - 'label': 'Use', - }, - { - 'name': 'automation_ids', - 'label': 'User ID', - 'type': 'combined', - 'combine': ['automation_ids_use', 'automation_ids'], - }, - ], - }, - ], -}] diff --git a/couchpotato/core/media/movie/providers/automation/flixster/main.py b/couchpotato/core/media/movie/providers/automation/flixster/main.py deleted file mode 100644 index cebe89d..0000000 --- a/couchpotato/core/media/movie/providers/automation/flixster/main.py +++ /dev/null @@ -1,50 +0,0 @@ -from couchpotato.core.helpers.variable import tryInt, splitString -from couchpotato.core.logger import CPLog -from couchpotato.core.media.movie.providers.automation.base import Automation - -log = CPLog(__name__) - - -class Flixster(Automation): - - url = 'http://www.flixster.com/api/users/%s/movies/ratings?scoreTypes=wts' - - interval = 60 - - def getIMDBids(self): - - ids = splitString(self.conf('automation_ids')) - - if len(ids) == 0: - return [] - - movies = [] - - for movie in self.getWatchlist(): - imdb_id = self.search(movie.get('title'), movie.get('year'), imdb_only = True) - movies.append(imdb_id) - - return movies - - def getWatchlist(self): - - enablers = [tryInt(x) for x in splitString(self.conf('automation_ids_use'))] - ids = splitString(self.conf('automation_ids')) - - index = -1 - movies = [] - for user_id in ids: - - index += 1 - if not enablers[index]: - continue - - data = self.getJsonData(self.url % user_id, decode_from = 'iso-8859-1') - - for movie in data: - movies.append({ - 'title': movie['movie']['title'], - 'year': movie['movie']['year'] - }) - - return movies diff --git a/couchpotato/core/media/movie/providers/automation/goodfilms.py b/couchpotato/core/media/movie/providers/automation/goodfilms.py new file mode 100644 index 0000000..2524f66 --- /dev/null +++ b/couchpotato/core/media/movie/providers/automation/goodfilms.py @@ -0,0 +1,81 @@ +from bs4 import BeautifulSoup +from couchpotato.core.logger import CPLog +from couchpotato.core.media.movie.providers.automation.base import Automation + +log = CPLog(__name__) + +autoload = 'Goodfilms' + + +class Goodfilms(Automation): + + url = 'https://goodfil.ms/%s/queue?page=%d&without_layout=1' + + interval = 1800 + + def getIMDBids(self): + + if not self.conf('automation_username'): + log.error('Please fill in your username') + return [] + + movies = [] + + for movie in self.getWatchlist(): + imdb_id = self.search(movie.get('title'), movie.get('year'), imdb_only = True) + movies.append(imdb_id) + + return movies + + def getWatchlist(self): + + movies = [] + page = 1 + + while True: + url = self.url % (self.conf('automation_username'), page) + data = self.getHTMLData(url) + soup = BeautifulSoup(data) + + this_watch_list = soup.find_all('div', attrs = { + 'class': 'movie', + 'data-film-title': True + }) + + if not this_watch_list: # No Movies + break + + for movie in this_watch_list: + movies.append({ 'title': movie['data-film-title'], 'year': movie['data-film-year'] }) + + if not 'next page' in data.lower(): + break + + page += 1 + + return movies + + +config = [{ + 'name': 'goodfilms', + 'groups': [ + { + 'tab': 'automation', + 'list': 'watchlist_providers', + 'name': 'goodfilms_automation', + 'label': 'Goodfilms', + 'description': 'import movies from your Goodfilms queue', + 'options': [ + { + 'name': 'automation_enabled', + 'default': False, + 'type': 'enabler', + }, + { + 'name': 'automation_username', + 'label': 'Username', + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/providers/automation/goodfilms/__init__.py b/couchpotato/core/media/movie/providers/automation/goodfilms/__init__.py deleted file mode 100644 index e04ccd0..0000000 --- a/couchpotato/core/media/movie/providers/automation/goodfilms/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -from .main import Goodfilms - - -def start(): - return Goodfilms() - -config = [{ - 'name': 'goodfilms', - 'groups': [ - { - 'tab': 'automation', - 'list': 'watchlist_providers', - 'name': 'goodfilms_automation', - 'label': 'Goodfilms', - 'description': 'import movies from your Goodfilms queue', - 'options': [ - { - 'name': 'automation_enabled', - 'default': False, - 'type': 'enabler', - }, - { - 'name': 'automation_username', - 'label': 'Username', - }, - ], - }, - ], -}] diff --git a/couchpotato/core/media/movie/providers/automation/goodfilms/main.py b/couchpotato/core/media/movie/providers/automation/goodfilms/main.py deleted file mode 100644 index 3ab51e0..0000000 --- a/couchpotato/core/media/movie/providers/automation/goodfilms/main.py +++ /dev/null @@ -1,54 +0,0 @@ -from bs4 import BeautifulSoup -from couchpotato.core.logger import CPLog -from couchpotato.core.media.movie.providers.automation.base import Automation - -log = CPLog(__name__) - - -class Goodfilms(Automation): - - url = 'https://goodfil.ms/%s/queue?page=%d&without_layout=1' - - interval = 1800 - - def getIMDBids(self): - - if not self.conf('automation_username'): - log.error('Please fill in your username') - return [] - - movies = [] - - for movie in self.getWatchlist(): - imdb_id = self.search(movie.get('title'), movie.get('year'), imdb_only = True) - movies.append(imdb_id) - - return movies - - def getWatchlist(self): - - movies = [] - page = 1 - - while True: - url = self.url % (self.conf('automation_username'), page) - data = self.getHTMLData(url) - soup = BeautifulSoup(data) - - this_watch_list = soup.find_all('div', attrs = { - 'class': 'movie', - 'data-film-title': True - }) - - if not this_watch_list: # No Movies - break - - for movie in this_watch_list: - movies.append({ 'title': movie['data-film-title'], 'year': movie['data-film-year'] }) - - if not 'next page' in data.lower(): - break - - page += 1 - - return movies diff --git a/couchpotato/core/media/movie/providers/automation/imdb.py b/couchpotato/core/media/movie/providers/automation/imdb.py new file mode 100644 index 0000000..d5b9225 --- /dev/null +++ b/couchpotato/core/media/movie/providers/automation/imdb.py @@ -0,0 +1,211 @@ +import traceback +import re + +from bs4 import BeautifulSoup +from couchpotato import fireEvent +from couchpotato.core.helpers.rss import RSS +from couchpotato.core.helpers.variable import getImdb, splitString, tryInt + +from couchpotato.core.logger import CPLog +from couchpotato.core.media._base.providers.base import MultiProvider +from couchpotato.core.media.movie.providers.automation.base import Automation + + +log = CPLog(__name__) + +autoload = 'IMDB' + + +class IMDB(MultiProvider): + + def getTypes(self): + return [IMDBWatchlist, IMDBAutomation] + + +class IMDBBase(Automation, RSS): + + interval = 1800 + + def getInfo(self, imdb_id): + return fireEvent('movie.info', identifier = imdb_id, extended = False, merge = True) + + +class IMDBWatchlist(IMDBBase): + + enabled_option = 'automation_enabled' + + def getIMDBids(self): + + movies = [] + + watchlist_enablers = [tryInt(x) for x in splitString(self.conf('automation_urls_use'))] + watchlist_urls = splitString(self.conf('automation_urls')) + + index = -1 + for watchlist_url in watchlist_urls: + + try: + # Get list ID + ids = re.findall('(?:list/|list_id=)([a-zA-Z0-9\-_]{11})', watchlist_url) + if len(ids) == 1: + watchlist_url = 'http://www.imdb.com/list/%s/?view=compact&sort=created:asc' % ids[0] + # Try find user id with watchlist + else: + userids = re.findall('(ur\d{7,9})', watchlist_url) + if len(userids) == 1: + watchlist_url = 'http://www.imdb.com/user/%s/watchlist?view=compact&sort=created:asc' % userids[0] + except: + log.error('Failed getting id from watchlist: %s', traceback.format_exc()) + + index += 1 + if not watchlist_enablers[index]: + continue + + start = 0 + while True: + try: + + w_url = '%s&start=%s' % (watchlist_url, start) + log.debug('Started IMDB watchlists: %s', w_url) + html = self.getHTMLData(w_url) + + try: + split = splitString(html, split_on="
")[1] + html = splitString(split, split_on="
")[0] + except: + pass + + imdbs = getImdb(html, multiple = True) if html else [] + + for imdb in imdbs: + if imdb not in movies: + movies.append(imdb) + + if self.shuttingDown(): + break + + log.debug('Found %s movies on %s', (len(imdbs), w_url)) + + if len(imdbs) < 250: + break + + start += 250 + + except: + log.error('Failed loading IMDB watchlist: %s %s', (watchlist_url, traceback.format_exc())) + + return movies + + +class IMDBAutomation(IMDBBase): + + enabled_option = 'automation_providers_enabled' + + chart_urls = { + 'theater': 'http://www.imdb.com/movies-in-theaters/', + 'top250': 'http://www.imdb.com/chart/top', + 'boxoffice': 'http://www.imdb.com/chart/', + } + + first_table = ['boxoffice'] + + def getIMDBids(self): + + movies = [] + + for url in self.chart_urls: + if self.conf('automation_charts_%s' % url): + data = self.getHTMLData(self.chart_urls[url]) + if data: + html = BeautifulSoup(data) + + try: + result_div = html.find('div', attrs = {'id': 'main'}) + + try: + if url in self.first_table: + table = result_div.find('table') + result_div = table if table else result_div + except: + pass + + imdb_ids = getImdb(str(result_div), multiple = True) + + for imdb_id in imdb_ids: + info = self.getInfo(imdb_id) + if info and self.isMinimalMovie(info): + movies.append(imdb_id) + + if self.shuttingDown(): + break + + except: + log.error('Failed loading IMDB chart results from %s: %s', (url, traceback.format_exc())) + + return movies + + +config = [{ + 'name': 'imdb', + 'groups': [ + { + 'tab': 'automation', + 'list': 'watchlist_providers', + 'name': 'imdb_automation_watchlist', + 'label': 'IMDB', + 'description': 'From any public IMDB watchlists.', + 'options': [ + { + 'name': 'automation_enabled', + 'default': False, + 'type': 'enabler', + }, + { + 'name': 'automation_urls_use', + 'label': 'Use', + }, + { + 'name': 'automation_urls', + 'label': 'url', + 'type': 'combined', + 'combine': ['automation_urls_use', 'automation_urls'], + }, + ], + }, + { + 'tab': 'automation', + 'list': 'automation_providers', + 'name': 'imdb_automation_charts', + 'label': 'IMDB', + 'description': 'Import movies from IMDB Charts', + 'options': [ + { + 'name': 'automation_providers_enabled', + 'default': False, + 'type': 'enabler', + }, + { + 'name': 'automation_charts_theater', + 'type': 'bool', + 'label': 'In Theaters', + 'description': 'New Movies In-Theaters chart', + 'default': True, + }, + { + 'name': 'automation_charts_top250', + 'type': 'bool', + 'label': 'TOP 250', + 'description': 'IMDB TOP 250 chart', + 'default': True, + }, + { + 'name': 'automation_charts_boxoffice', + 'type': 'bool', + 'label': 'Box office TOP 10', + 'description': 'IMDB Box office TOP 10 chart', + 'default': True, + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/providers/automation/imdb/__init__.py b/couchpotato/core/media/movie/providers/automation/imdb/__init__.py deleted file mode 100644 index f9baabf..0000000 --- a/couchpotato/core/media/movie/providers/automation/imdb/__init__.py +++ /dev/null @@ -1,70 +0,0 @@ -from .main import IMDB - - -def start(): - return IMDB() - -config = [{ - 'name': 'imdb', - 'groups': [ - { - 'tab': 'automation', - 'list': 'watchlist_providers', - 'name': 'imdb_automation_watchlist', - 'label': 'IMDB', - 'description': 'From any public IMDB watchlists.', - 'options': [ - { - 'name': 'automation_enabled', - 'default': False, - 'type': 'enabler', - }, - { - 'name': 'automation_urls_use', - 'label': 'Use', - }, - { - 'name': 'automation_urls', - 'label': 'url', - 'type': 'combined', - 'combine': ['automation_urls_use', 'automation_urls'], - }, - ], - }, - { - 'tab': 'automation', - 'list': 'automation_providers', - 'name': 'imdb_automation_charts', - 'label': 'IMDB', - 'description': 'Import movies from IMDB Charts', - 'options': [ - { - 'name': 'automation_providers_enabled', - 'default': False, - 'type': 'enabler', - }, - { - 'name': 'automation_charts_theater', - 'type': 'bool', - 'label': 'In Theaters', - 'description': 'New Movies In-Theaters chart', - 'default': True, - }, - { - 'name': 'automation_charts_top250', - 'type': 'bool', - 'label': 'TOP 250', - 'description': 'IMDB TOP 250 chart', - 'default': True, - }, - { - 'name': 'automation_charts_boxoffice', - 'type': 'bool', - 'label': 'Box office TOP 10', - 'description': 'IMDB Box office TOP 10 chart', - 'default': True, - }, - ], - }, - ], -}] diff --git a/couchpotato/core/media/movie/providers/automation/imdb/main.py b/couchpotato/core/media/movie/providers/automation/imdb/main.py deleted file mode 100644 index 9b25e18..0000000 --- a/couchpotato/core/media/movie/providers/automation/imdb/main.py +++ /dev/null @@ -1,143 +0,0 @@ -import traceback -import re - -from bs4 import BeautifulSoup -from couchpotato import fireEvent -from couchpotato.core.helpers.rss import RSS -from couchpotato.core.helpers.variable import getImdb, splitString, tryInt - -from couchpotato.core.logger import CPLog -from couchpotato.core.media._base.providers.base import MultiProvider -from couchpotato.core.media.movie.providers.automation.base import Automation - - -log = CPLog(__name__) - - -class IMDB(MultiProvider): - - def getTypes(self): - return [IMDBWatchlist, IMDBAutomation] - - -class IMDBBase(Automation, RSS): - - interval = 1800 - - def getInfo(self, imdb_id): - return fireEvent('movie.info', identifier = imdb_id, extended = False, merge = True) - - -class IMDBWatchlist(IMDBBase): - - enabled_option = 'automation_enabled' - - def getIMDBids(self): - - movies = [] - - watchlist_enablers = [tryInt(x) for x in splitString(self.conf('automation_urls_use'))] - watchlist_urls = splitString(self.conf('automation_urls')) - - index = -1 - for watchlist_url in watchlist_urls: - - try: - # Get list ID - ids = re.findall('(?:list/|list_id=)([a-zA-Z0-9\-_]{11})', watchlist_url) - if len(ids) == 1: - watchlist_url = 'http://www.imdb.com/list/%s/?view=compact&sort=created:asc' % ids[0] - # Try find user id with watchlist - else: - userids = re.findall('(ur\d{7,9})', watchlist_url) - if len(userids) == 1: - watchlist_url = 'http://www.imdb.com/user/%s/watchlist?view=compact&sort=created:asc' % userids[0] - except: - log.error('Failed getting id from watchlist: %s', traceback.format_exc()) - - index += 1 - if not watchlist_enablers[index]: - continue - - start = 0 - while True: - try: - - w_url = '%s&start=%s' % (watchlist_url, start) - log.debug('Started IMDB watchlists: %s', w_url) - html = self.getHTMLData(w_url) - - try: - split = splitString(html, split_on="
")[1] - html = splitString(split, split_on="
")[0] - except: - pass - - imdbs = getImdb(html, multiple = True) if html else [] - - for imdb in imdbs: - if imdb not in movies: - movies.append(imdb) - - if self.shuttingDown(): - break - - log.debug('Found %s movies on %s', (len(imdbs), w_url)) - - if len(imdbs) < 250: - break - - start += 250 - - except: - log.error('Failed loading IMDB watchlist: %s %s', (watchlist_url, traceback.format_exc())) - - return movies - - -class IMDBAutomation(IMDBBase): - - enabled_option = 'automation_providers_enabled' - - chart_urls = { - 'theater': 'http://www.imdb.com/movies-in-theaters/', - 'top250': 'http://www.imdb.com/chart/top', - 'boxoffice': 'http://www.imdb.com/chart/', - } - - first_table = ['boxoffice'] - - def getIMDBids(self): - - movies = [] - - for url in self.chart_urls: - if self.conf('automation_charts_%s' % url): - data = self.getHTMLData(self.chart_urls[url]) - if data: - html = BeautifulSoup(data) - - try: - result_div = html.find('div', attrs = {'id': 'main'}) - - try: - if url in self.first_table: - table = result_div.find('table') - result_div = table if table else result_div - except: - pass - - imdb_ids = getImdb(str(result_div), multiple = True) - - for imdb_id in imdb_ids: - info = self.getInfo(imdb_id) - if info and self.isMinimalMovie(info): - movies.append(imdb_id) - - if self.shuttingDown(): - break - - except: - log.error('Failed loading IMDB chart results from %s: %s', (url, traceback.format_exc())) - - return movies diff --git a/couchpotato/core/media/movie/providers/automation/itunes.py b/couchpotato/core/media/movie/providers/automation/itunes.py new file mode 100644 index 0000000..1feb96f --- /dev/null +++ b/couchpotato/core/media/movie/providers/automation/itunes.py @@ -0,0 +1,95 @@ +from couchpotato.core.helpers.rss import RSS +from couchpotato.core.helpers.variable import md5, splitString, tryInt +from couchpotato.core.logger import CPLog +from couchpotato.core.media.movie.providers.automation.base import Automation +from xml.etree.ElementTree import QName +import datetime +import traceback +import xml.etree.ElementTree as XMLTree + +log = CPLog(__name__) + +autoload = 'ITunes' + + +class ITunes(Automation, RSS): + + interval = 1800 + + def getIMDBids(self): + + movies = [] + + enablers = [tryInt(x) for x in splitString(self.conf('automation_urls_use'))] + urls = splitString(self.conf('automation_urls')) + + namespace = 'http://www.w3.org/2005/Atom' + namespace_im = 'https://rss.itunes.apple.com' + + index = -1 + for url in urls: + + index += 1 + if len(enablers) == 0 or len(enablers) < index or not enablers[index]: + continue + + try: + cache_key = 'itunes.rss.%s' % md5(url) + rss_data = self.getCache(cache_key, url) + + data = XMLTree.fromstring(rss_data) + + if data is not None: + entry_tag = str(QName(namespace, 'entry')) + rss_movies = self.getElements(data, entry_tag) + + for movie in rss_movies: + name_tag = str(QName(namespace_im, 'name')) + name = self.getTextElement(movie, name_tag) + + releaseDate_tag = str(QName(namespace_im, 'releaseDate')) + releaseDateText = self.getTextElement(movie, releaseDate_tag) + year = datetime.datetime.strptime(releaseDateText, '%Y-%m-%dT00:00:00-07:00').strftime("%Y") + + imdb = self.search(name, year) + + if imdb and self.isMinimalMovie(imdb): + movies.append(imdb['imdb']) + + except: + log.error('Failed loading iTunes rss feed: %s %s', (url, traceback.format_exc())) + + return movies + + +config = [{ + 'name': 'itunes', + 'groups': [ + { + 'tab': 'automation', + 'list': 'automation_providers', + 'name': 'itunes_automation', + 'label': 'iTunes', + 'description': 'From any iTunes Store feed. Url should be the RSS link.', + 'options': [ + { + 'name': 'automation_enabled', + 'default': False, + 'type': 'enabler', + }, + { + 'name': 'automation_urls_use', + 'label': 'Use', + 'default': ',', + }, + { + 'name': 'automation_urls', + 'label': 'url', + 'type': 'combined', + 'combine': ['automation_urls_use', 'automation_urls'], + 'default': 'https://itunes.apple.com/rss/topmovies/limit=25/xml,', + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/providers/automation/itunes/__init__.py b/couchpotato/core/media/movie/providers/automation/itunes/__init__.py deleted file mode 100644 index 13526f4..0000000 --- a/couchpotato/core/media/movie/providers/automation/itunes/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -from .main import ITunes - - -def start(): - return ITunes() - -config = [{ - 'name': 'itunes', - 'groups': [ - { - 'tab': 'automation', - 'list': 'automation_providers', - 'name': 'itunes_automation', - 'label': 'iTunes', - 'description': 'From any iTunes Store feed. Url should be the RSS link.', - 'options': [ - { - 'name': 'automation_enabled', - 'default': False, - 'type': 'enabler', - }, - { - 'name': 'automation_urls_use', - 'label': 'Use', - 'default': ',', - }, - { - 'name': 'automation_urls', - 'label': 'url', - 'type': 'combined', - 'combine': ['automation_urls_use', 'automation_urls'], - 'default': 'https://itunes.apple.com/rss/topmovies/limit=25/xml,', - }, - ], - }, - ], -}] diff --git a/couchpotato/core/media/movie/providers/automation/itunes/main.py b/couchpotato/core/media/movie/providers/automation/itunes/main.py deleted file mode 100644 index 3257a66..0000000 --- a/couchpotato/core/media/movie/providers/automation/itunes/main.py +++ /dev/null @@ -1,60 +0,0 @@ -from couchpotato.core.helpers.rss import RSS -from couchpotato.core.helpers.variable import md5, splitString, tryInt -from couchpotato.core.logger import CPLog -from couchpotato.core.media.movie.providers.automation.base import Automation -from xml.etree.ElementTree import QName -import datetime -import traceback -import xml.etree.ElementTree as XMLTree - -log = CPLog(__name__) - - -class ITunes(Automation, RSS): - - interval = 1800 - - def getIMDBids(self): - - movies = [] - - enablers = [tryInt(x) for x in splitString(self.conf('automation_urls_use'))] - urls = splitString(self.conf('automation_urls')) - - namespace = 'http://www.w3.org/2005/Atom' - namespace_im = 'https://rss.itunes.apple.com' - - index = -1 - for url in urls: - - index += 1 - if len(enablers) == 0 or len(enablers) < index or not enablers[index]: - continue - - try: - cache_key = 'itunes.rss.%s' % md5(url) - rss_data = self.getCache(cache_key, url) - - data = XMLTree.fromstring(rss_data) - - if data is not None: - entry_tag = str(QName(namespace, 'entry')) - rss_movies = self.getElements(data, entry_tag) - - for movie in rss_movies: - name_tag = str(QName(namespace_im, 'name')) - name = self.getTextElement(movie, name_tag) - - releaseDate_tag = str(QName(namespace_im, 'releaseDate')) - releaseDateText = self.getTextElement(movie, releaseDate_tag) - year = datetime.datetime.strptime(releaseDateText, '%Y-%m-%dT00:00:00-07:00').strftime("%Y") - - imdb = self.search(name, year) - - if imdb and self.isMinimalMovie(imdb): - movies.append(imdb['imdb']) - - except: - log.error('Failed loading iTunes rss feed: %s %s', (url, traceback.format_exc())) - - return movies diff --git a/couchpotato/core/media/movie/providers/automation/kinepolis.py b/couchpotato/core/media/movie/providers/automation/kinepolis.py new file mode 100644 index 0000000..9350572 --- /dev/null +++ b/couchpotato/core/media/movie/providers/automation/kinepolis.py @@ -0,0 +1,52 @@ +from couchpotato.core.helpers.rss import RSS +from couchpotato.core.logger import CPLog +from couchpotato.core.media.movie.providers.automation.base import Automation +import datetime + +log = CPLog(__name__) + +autoload = 'Kinepolis' + + +class Kinepolis(Automation, RSS): + + interval = 1800 + rss_url = 'http://kinepolis.be/nl/top10-box-office/feed' + + def getIMDBids(self): + + movies = [] + + rss_movies = self.getRSSData(self.rss_url) + + for movie in rss_movies: + name = self.getTextElement(movie, 'title') + year = datetime.datetime.now().strftime('%Y') + + imdb = self.search(name, year) + + if imdb and self.isMinimalMovie(imdb): + movies.append(imdb['imdb']) + + return movies + + +config = [{ + 'name': 'kinepolis', + 'groups': [ + { + 'tab': 'automation', + 'list': 'automation_providers', + 'name': 'kinepolis_automation', + 'label': 'Kinepolis', + 'description': 'Imports movies from the current top 10 of kinepolis.', + 'options': [ + { + 'name': 'automation_enabled', + 'default': False, + 'type': 'enabler', + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/providers/automation/kinepolis/__init__.py b/couchpotato/core/media/movie/providers/automation/kinepolis/__init__.py deleted file mode 100644 index cc4c570..0000000 --- a/couchpotato/core/media/movie/providers/automation/kinepolis/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -from .main import Kinepolis - - -def start(): - return Kinepolis() - -config = [{ - 'name': 'kinepolis', - 'groups': [ - { - 'tab': 'automation', - 'list': 'automation_providers', - 'name': 'kinepolis_automation', - 'label': 'Kinepolis', - 'description': 'Imports movies from the current top 10 of kinepolis.', - 'options': [ - { - 'name': 'automation_enabled', - 'default': False, - 'type': 'enabler', - }, - ], - }, - ], -}] diff --git a/couchpotato/core/media/movie/providers/automation/kinepolis/main.py b/couchpotato/core/media/movie/providers/automation/kinepolis/main.py deleted file mode 100644 index a1e099e..0000000 --- a/couchpotato/core/media/movie/providers/automation/kinepolis/main.py +++ /dev/null @@ -1,29 +0,0 @@ -from couchpotato.core.helpers.rss import RSS -from couchpotato.core.logger import CPLog -from couchpotato.core.media.movie.providers.automation.base import Automation -import datetime - -log = CPLog(__name__) - - -class Kinepolis(Automation, RSS): - - interval = 1800 - rss_url = 'http://kinepolis.be/nl/top10-box-office/feed' - - def getIMDBids(self): - - movies = [] - - rss_movies = self.getRSSData(self.rss_url) - - for movie in rss_movies: - name = self.getTextElement(movie, 'title') - year = datetime.datetime.now().strftime('%Y') - - imdb = self.search(name, year) - - if imdb and self.isMinimalMovie(imdb): - movies.append(imdb['imdb']) - - return movies diff --git a/couchpotato/core/media/movie/providers/automation/letterboxd.py b/couchpotato/core/media/movie/providers/automation/letterboxd.py new file mode 100644 index 0000000..55a9549 --- /dev/null +++ b/couchpotato/core/media/movie/providers/automation/letterboxd.py @@ -0,0 +1,84 @@ +from bs4 import BeautifulSoup +from couchpotato.core.helpers.variable import tryInt, splitString, removeEmpty +from couchpotato.core.logger import CPLog +from couchpotato.core.media.movie.providers.automation.base import Automation +import re + +log = CPLog(__name__) + +autoload = 'Letterboxd' + + +class Letterboxd(Automation): + + url = 'http://letterboxd.com/%s/watchlist/' + pattern = re.compile(r'(.*)\((\d*)\)') + + interval = 1800 + + def getIMDBids(self): + + urls = splitString(self.conf('automation_urls')) + + if len(urls) == 0: + return [] + + movies = [] + + for movie in self.getWatchlist(): + imdb_id = self.search(movie.get('title'), movie.get('year'), imdb_only = True) + movies.append(imdb_id) + + return movies + + def getWatchlist(self): + + enablers = [tryInt(x) for x in splitString(self.conf('automation_urls_use'))] + urls = splitString(self.conf('automation_urls')) + + index = -1 + movies = [] + for username in urls: + + index += 1 + if not enablers[index]: + continue + + soup = BeautifulSoup(self.getHTMLData(self.url % username)) + + for movie in soup.find_all('a', attrs = {'class': 'frame'}): + match = removeEmpty(self.pattern.split(movie['title'])) + movies.append({'title': match[0], 'year': match[1] }) + + return movies + + +config = [{ + 'name': 'letterboxd', + 'groups': [ + { + 'tab': 'automation', + 'list': 'watchlist_providers', + 'name': 'letterboxd_automation', + 'label': 'Letterboxd', + 'description': 'Import movies from any public Letterboxd watchlist', + 'options': [ + { + 'name': 'automation_enabled', + 'default': False, + 'type': 'enabler', + }, + { + 'name': 'automation_urls_use', + 'label': 'Use', + }, + { + 'name': 'automation_urls', + 'label': 'Username', + 'type': 'combined', + 'combine': ['automation_urls_use', 'automation_urls'], + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/providers/automation/letterboxd/__init__.py b/couchpotato/core/media/movie/providers/automation/letterboxd/__init__.py deleted file mode 100644 index 88bfe6a..0000000 --- a/couchpotato/core/media/movie/providers/automation/letterboxd/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -from .main import Letterboxd - - -def start(): - return Letterboxd() - -config = [{ - 'name': 'letterboxd', - 'groups': [ - { - 'tab': 'automation', - 'list': 'watchlist_providers', - 'name': 'letterboxd_automation', - 'label': 'Letterboxd', - 'description': 'Import movies from any public Letterboxd watchlist', - 'options': [ - { - 'name': 'automation_enabled', - 'default': False, - 'type': 'enabler', - }, - { - 'name': 'automation_urls_use', - 'label': 'Use', - }, - { - 'name': 'automation_urls', - 'label': 'Username', - 'type': 'combined', - 'combine': ['automation_urls_use', 'automation_urls'], - }, - ], - }, - ], -}] diff --git a/couchpotato/core/media/movie/providers/automation/letterboxd/main.py b/couchpotato/core/media/movie/providers/automation/letterboxd/main.py deleted file mode 100644 index 70e574f..0000000 --- a/couchpotato/core/media/movie/providers/automation/letterboxd/main.py +++ /dev/null @@ -1,51 +0,0 @@ -from bs4 import BeautifulSoup -from couchpotato.core.helpers.variable import tryInt, splitString, removeEmpty -from couchpotato.core.logger import CPLog -from couchpotato.core.media.movie.providers.automation.base import Automation -import re - -log = CPLog(__name__) - - -class Letterboxd(Automation): - - url = 'http://letterboxd.com/%s/watchlist/' - pattern = re.compile(r'(.*)\((\d*)\)') - - interval = 1800 - - def getIMDBids(self): - - urls = splitString(self.conf('automation_urls')) - - if len(urls) == 0: - return [] - - movies = [] - - for movie in self.getWatchlist(): - imdb_id = self.search(movie.get('title'), movie.get('year'), imdb_only = True) - movies.append(imdb_id) - - return movies - - def getWatchlist(self): - - enablers = [tryInt(x) for x in splitString(self.conf('automation_urls_use'))] - urls = splitString(self.conf('automation_urls')) - - index = -1 - movies = [] - for username in urls: - - index += 1 - if not enablers[index]: - continue - - soup = BeautifulSoup(self.getHTMLData(self.url % username)) - - for movie in soup.find_all('a', attrs = {'class': 'frame'}): - match = removeEmpty(self.pattern.split(movie['title'])) - movies.append({'title': match[0], 'year': match[1] }) - - return movies diff --git a/couchpotato/core/media/movie/providers/automation/moviemeter.py b/couchpotato/core/media/movie/providers/automation/moviemeter.py new file mode 100644 index 0000000..883fcfa --- /dev/null +++ b/couchpotato/core/media/movie/providers/automation/moviemeter.py @@ -0,0 +1,51 @@ +from couchpotato.core.event import fireEvent +from couchpotato.core.helpers.rss import RSS +from couchpotato.core.logger import CPLog +from couchpotato.core.media.movie.providers.automation.base import Automation + +log = CPLog(__name__) + +autoload = 'Moviemeter' + + +class Moviemeter(Automation, RSS): + + interval = 1800 + rss_url = 'http://www.moviemeter.nl/rss/cinema' + + def getIMDBids(self): + + movies = [] + + rss_movies = self.getRSSData(self.rss_url) + + for movie in rss_movies: + + name_year = fireEvent('scanner.name_year', self.getTextElement(movie, 'title'), single = True) + imdb = self.search(name_year.get('name'), name_year.get('year')) + + if imdb and self.isMinimalMovie(imdb): + movies.append(imdb['imdb']) + + return movies + + +config = [{ + 'name': 'moviemeter', + 'groups': [ + { + 'tab': 'automation', + 'list': 'automation_providers', + 'name': 'moviemeter_automation', + 'label': 'Moviemeter', + 'description': 'Imports movies from the current top 10 of moviemeter.nl.', + 'options': [ + { + 'name': 'automation_enabled', + 'default': False, + 'type': 'enabler', + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/providers/automation/moviemeter/__init__.py b/couchpotato/core/media/movie/providers/automation/moviemeter/__init__.py deleted file mode 100644 index 0e9a4ed..0000000 --- a/couchpotato/core/media/movie/providers/automation/moviemeter/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -from .main import Moviemeter - - -def start(): - return Moviemeter() - -config = [{ - 'name': 'moviemeter', - 'groups': [ - { - 'tab': 'automation', - 'list': 'automation_providers', - 'name': 'moviemeter_automation', - 'label': 'Moviemeter', - 'description': 'Imports movies from the current top 10 of moviemeter.nl.', - 'options': [ - { - 'name': 'automation_enabled', - 'default': False, - 'type': 'enabler', - }, - ], - }, - ], -}] diff --git a/couchpotato/core/media/movie/providers/automation/moviemeter/main.py b/couchpotato/core/media/movie/providers/automation/moviemeter/main.py deleted file mode 100644 index 890c8bc..0000000 --- a/couchpotato/core/media/movie/providers/automation/moviemeter/main.py +++ /dev/null @@ -1,28 +0,0 @@ -from couchpotato.core.event import fireEvent -from couchpotato.core.helpers.rss import RSS -from couchpotato.core.logger import CPLog -from couchpotato.core.media.movie.providers.automation.base import Automation - -log = CPLog(__name__) - - -class Moviemeter(Automation, RSS): - - interval = 1800 - rss_url = 'http://www.moviemeter.nl/rss/cinema' - - def getIMDBids(self): - - movies = [] - - rss_movies = self.getRSSData(self.rss_url) - - for movie in rss_movies: - - name_year = fireEvent('scanner.name_year', self.getTextElement(movie, 'title'), single = True) - imdb = self.search(name_year.get('name'), name_year.get('year')) - - if imdb and self.isMinimalMovie(imdb): - movies.append(imdb['imdb']) - - return movies diff --git a/couchpotato/core/media/movie/providers/automation/movies_io.py b/couchpotato/core/media/movie/providers/automation/movies_io.py new file mode 100644 index 0000000..3b0b54f --- /dev/null +++ b/couchpotato/core/media/movie/providers/automation/movies_io.py @@ -0,0 +1,72 @@ +from couchpotato.core.event import fireEvent +from couchpotato.core.helpers.rss import RSS +from couchpotato.core.helpers.variable import tryInt, splitString +from couchpotato.core.logger import CPLog +from couchpotato.core.media.movie.providers.automation.base import Automation + +log = CPLog(__name__) + +autoload = 'MoviesIO' + + +class MoviesIO(Automation, RSS): + + interval = 1800 + + def getIMDBids(self): + + movies = [] + + enablers = [tryInt(x) for x in splitString(self.conf('automation_urls_use'))] + + index = -1 + for rss_url in splitString(self.conf('automation_urls')): + + index += 1 + if not enablers[index]: + continue + + rss_movies = self.getRSSData(rss_url, headers = {'Referer': ''}) + + for movie in rss_movies: + + nameyear = fireEvent('scanner.name_year', self.getTextElement(movie, 'title'), single = True) + imdb = self.search(nameyear.get('name'), nameyear.get('year'), imdb_only = True) + + if not imdb: + continue + + movies.append(imdb) + + return movies + + +config = [{ + 'name': 'moviesio', + 'groups': [ + { + 'tab': 'automation', + 'list': 'watchlist_providers', + 'name': 'moviesio', + 'label': 'Movies.IO', + 'description': 'Imports movies from Movies.io RSS watchlists', + 'options': [ + { + 'name': 'automation_enabled', + 'default': False, + 'type': 'enabler', + }, + { + 'name': 'automation_urls_use', + 'label': 'Use', + }, + { + 'name': 'automation_urls', + 'label': 'url', + 'type': 'combined', + 'combine': ['automation_urls_use', 'automation_urls'], + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/providers/automation/movies_io/__init__.py b/couchpotato/core/media/movie/providers/automation/movies_io/__init__.py deleted file mode 100644 index 0361223..0000000 --- a/couchpotato/core/media/movie/providers/automation/movies_io/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -from .main import MoviesIO - - -def start(): - return MoviesIO() - -config = [{ - 'name': 'moviesio', - 'groups': [ - { - 'tab': 'automation', - 'list': 'watchlist_providers', - 'name': 'moviesio', - 'label': 'Movies.IO', - 'description': 'Imports movies from Movies.io RSS watchlists', - 'options': [ - { - 'name': 'automation_enabled', - 'default': False, - 'type': 'enabler', - }, - { - 'name': 'automation_urls_use', - 'label': 'Use', - }, - { - 'name': 'automation_urls', - 'label': 'url', - 'type': 'combined', - 'combine': ['automation_urls_use', 'automation_urls'], - }, - ], - }, - ], -}] diff --git a/couchpotato/core/media/movie/providers/automation/movies_io/main.py b/couchpotato/core/media/movie/providers/automation/movies_io/main.py deleted file mode 100644 index 890cbde..0000000 --- a/couchpotato/core/media/movie/providers/automation/movies_io/main.py +++ /dev/null @@ -1,39 +0,0 @@ -from couchpotato.core.event import fireEvent -from couchpotato.core.helpers.rss import RSS -from couchpotato.core.helpers.variable import tryInt, splitString -from couchpotato.core.logger import CPLog -from couchpotato.core.media.movie.providers.automation.base import Automation - -log = CPLog(__name__) - - -class MoviesIO(Automation, RSS): - - interval = 1800 - - def getIMDBids(self): - - movies = [] - - enablers = [tryInt(x) for x in splitString(self.conf('automation_urls_use'))] - - index = -1 - for rss_url in splitString(self.conf('automation_urls')): - - index += 1 - if not enablers[index]: - continue - - rss_movies = self.getRSSData(rss_url, headers = {'Referer': ''}) - - for movie in rss_movies: - - nameyear = fireEvent('scanner.name_year', self.getTextElement(movie, 'title'), single = True) - imdb = self.search(nameyear.get('name'), nameyear.get('year'), imdb_only = True) - - if not imdb: - continue - - movies.append(imdb) - - return movies diff --git a/couchpotato/core/media/movie/providers/automation/rottentomatoes.py b/couchpotato/core/media/movie/providers/automation/rottentomatoes.py new file mode 100644 index 0000000..279b4f8 --- /dev/null +++ b/couchpotato/core/media/movie/providers/automation/rottentomatoes.py @@ -0,0 +1,94 @@ +from couchpotato.core.helpers.rss import RSS +from couchpotato.core.helpers.variable import tryInt, splitString +from couchpotato.core.logger import CPLog +from couchpotato.core.media.movie.providers.automation.base import Automation +from xml.etree.ElementTree import QName +import datetime +import re + +log = CPLog(__name__) + +autoload = 'Rottentomatoes' + + +class Rottentomatoes(Automation, RSS): + + interval = 1800 + + def getIMDBids(self): + + movies = [] + + rotten_tomatoes_namespace = 'http://www.rottentomatoes.com/xmlns/rtmovie/' + urls = dict(zip(splitString(self.conf('automation_urls')), [tryInt(x) for x in splitString(self.conf('automation_urls_use'))])) + + for url in urls: + + if not urls[url]: + continue + + rss_movies = self.getRSSData(url) + rating_tag = str(QName(rotten_tomatoes_namespace, 'tomatometer_percent')) + + for movie in rss_movies: + + value = self.getTextElement(movie, "title") + result = re.search('(?<=%\s).*', value) + + if result: + + log.info2('Something smells...') + rating = tryInt(self.getTextElement(movie, rating_tag)) + name = result.group(0) + + if rating < tryInt(self.conf('tomatometer_percent')): + log.info2('%s seems to be rotten...', name) + else: + + log.info2('Found %s fresh enough movies, enqueuing: %s', (rating, name)) + year = datetime.datetime.now().strftime("%Y") + imdb = self.search(name, year) + + if imdb and self.isMinimalMovie(imdb): + movies.append(imdb['imdb']) + + return movies + + +config = [{ + 'name': 'rottentomatoes', + 'groups': [ + { + 'tab': 'automation', + 'list': 'automation_providers', + 'name': 'rottentomatoes_automation', + 'label': 'Rottentomatoes', + 'description': 'Imports movies from rottentomatoes rss feeds specified below.', + 'options': [ + { + 'name': 'automation_enabled', + 'default': False, + 'type': 'enabler', + }, + { + 'name': 'automation_urls_use', + 'label': 'Use', + 'default': '1', + }, + { + 'name': 'automation_urls', + 'label': 'url', + 'type': 'combined', + 'combine': ['automation_urls_use', 'automation_urls'], + 'default': 'http://www.rottentomatoes.com/syndication/rss/in_theaters.xml', + }, + { + 'name': 'tomatometer_percent', + 'default': '80', + 'label': 'Tomatometer', + 'description': 'Use as extra scoring requirement', + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/providers/automation/rottentomatoes/__init__.py b/couchpotato/core/media/movie/providers/automation/rottentomatoes/__init__.py deleted file mode 100644 index 1d3026d..0000000 --- a/couchpotato/core/media/movie/providers/automation/rottentomatoes/__init__.py +++ /dev/null @@ -1,43 +0,0 @@ -from .main import Rottentomatoes - - -def start(): - return Rottentomatoes() - -config = [{ - 'name': 'rottentomatoes', - 'groups': [ - { - 'tab': 'automation', - 'list': 'automation_providers', - 'name': 'rottentomatoes_automation', - 'label': 'Rottentomatoes', - 'description': 'Imports movies from rottentomatoes rss feeds specified below.', - 'options': [ - { - 'name': 'automation_enabled', - 'default': False, - 'type': 'enabler', - }, - { - 'name': 'automation_urls_use', - 'label': 'Use', - 'default': '1', - }, - { - 'name': 'automation_urls', - 'label': 'url', - 'type': 'combined', - 'combine': ['automation_urls_use', 'automation_urls'], - 'default': 'http://www.rottentomatoes.com/syndication/rss/in_theaters.xml', - }, - { - 'name': 'tomatometer_percent', - 'default': '80', - 'label': 'Tomatometer', - 'description': 'Use as extra scoring requirement', - }, - ], - }, - ], -}] diff --git a/couchpotato/core/media/movie/providers/automation/rottentomatoes/main.py b/couchpotato/core/media/movie/providers/automation/rottentomatoes/main.py deleted file mode 100644 index 56e0e7c..0000000 --- a/couchpotato/core/media/movie/providers/automation/rottentomatoes/main.py +++ /dev/null @@ -1,53 +0,0 @@ -from couchpotato.core.helpers.rss import RSS -from couchpotato.core.helpers.variable import tryInt, splitString -from couchpotato.core.logger import CPLog -from couchpotato.core.media.movie.providers.automation.base import Automation -from xml.etree.ElementTree import QName -import datetime -import re - -log = CPLog(__name__) - - -class Rottentomatoes(Automation, RSS): - - interval = 1800 - - def getIMDBids(self): - - movies = [] - - rotten_tomatoes_namespace = 'http://www.rottentomatoes.com/xmlns/rtmovie/' - urls = dict(zip(splitString(self.conf('automation_urls')), [tryInt(x) for x in splitString(self.conf('automation_urls_use'))])) - - for url in urls: - - if not urls[url]: - continue - - rss_movies = self.getRSSData(url) - rating_tag = str(QName(rotten_tomatoes_namespace, 'tomatometer_percent')) - - for movie in rss_movies: - - value = self.getTextElement(movie, "title") - result = re.search('(?<=%\s).*', value) - - if result: - - log.info2('Something smells...') - rating = tryInt(self.getTextElement(movie, rating_tag)) - name = result.group(0) - - if rating < tryInt(self.conf('tomatometer_percent')): - log.info2('%s seems to be rotten...', name) - else: - - log.info2('Found %s fresh enough movies, enqueuing: %s', (rating, name)) - year = datetime.datetime.now().strftime("%Y") - imdb = self.search(name, year) - - if imdb and self.isMinimalMovie(imdb): - movies.append(imdb['imdb']) - - return movies diff --git a/couchpotato/core/media/movie/providers/automation/trakt.py b/couchpotato/core/media/movie/providers/automation/trakt.py new file mode 100644 index 0000000..0c94552 --- /dev/null +++ b/couchpotato/core/media/movie/providers/automation/trakt.py @@ -0,0 +1,81 @@ +from couchpotato.core.event import addEvent +from couchpotato.core.helpers.variable import sha1 +from couchpotato.core.logger import CPLog +from couchpotato.core.media.movie.providers.automation.base import Automation +import base64 + +log = CPLog(__name__) + +autoload = 'Trakt' + + +class Trakt(Automation): + + urls = { + 'base': 'http://api.trakt.tv/', + 'watchlist': 'user/watchlist/movies.json/%s/', + } + + def __init__(self): + super(Trakt, self).__init__() + + addEvent('setting.save.trakt.automation_password', self.sha1Password) + + def sha1Password(self, value): + return sha1(value) if value else '' + + def getIMDBids(self): + + movies = [] + for movie in self.getWatchlist(): + movies.append(movie.get('imdb_id')) + + return movies + + def getWatchlist(self): + method = (self.urls['watchlist'] % self.conf('automation_api_key')) + self.conf('automation_username') + return self.call(method) + + def call(self, method_url): + + headers = {} + if self.conf('automation_password'): + headers['Authorization'] = 'Basic %s' % base64.encodestring('%s:%s' % (self.conf('automation_username'), self.conf('automation_password')))[:-1] + + data = self.getJsonData(self.urls['base'] + method_url, headers = headers) + return data if data else [] + + +config = [{ + 'name': 'trakt', + 'groups': [ + { + 'tab': 'automation', + 'list': 'watchlist_providers', + 'name': 'trakt_automation', + 'label': 'Trakt', + 'description': 'import movies from your own watchlist', + 'options': [ + { + 'name': 'automation_enabled', + 'default': False, + 'type': 'enabler', + }, + { + 'name': 'automation_api_key', + 'label': 'Apikey', + }, + { + 'name': 'automation_username', + 'label': 'Username', + }, + { + 'name': 'automation_password', + 'label': 'Password', + 'type': 'password', + 'description': 'When you have "Protect my data" checked on trakt.', + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/providers/automation/trakt/__init__.py b/couchpotato/core/media/movie/providers/automation/trakt/__init__.py deleted file mode 100644 index 6ae2806..0000000 --- a/couchpotato/core/media/movie/providers/automation/trakt/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -from .main import Trakt - - -def start(): - return Trakt() - -config = [{ - 'name': 'trakt', - 'groups': [ - { - 'tab': 'automation', - 'list': 'watchlist_providers', - 'name': 'trakt_automation', - 'label': 'Trakt', - 'description': 'import movies from your own watchlist', - 'options': [ - { - 'name': 'automation_enabled', - 'default': False, - 'type': 'enabler', - }, - { - 'name': 'automation_api_key', - 'label': 'Apikey', - }, - { - 'name': 'automation_username', - 'label': 'Username', - }, - { - 'name': 'automation_password', - 'label': 'Password', - 'type': 'password', - 'description': 'When you have "Protect my data" checked on trakt.', - }, - ], - }, - ], -}] diff --git a/couchpotato/core/media/movie/providers/automation/trakt/main.py b/couchpotato/core/media/movie/providers/automation/trakt/main.py deleted file mode 100644 index 881b14a..0000000 --- a/couchpotato/core/media/movie/providers/automation/trakt/main.py +++ /dev/null @@ -1,44 +0,0 @@ -from couchpotato.core.event import addEvent -from couchpotato.core.helpers.variable import sha1 -from couchpotato.core.logger import CPLog -from couchpotato.core.media.movie.providers.automation.base import Automation -import base64 - -log = CPLog(__name__) - - -class Trakt(Automation): - - urls = { - 'base': 'http://api.trakt.tv/', - 'watchlist': 'user/watchlist/movies.json/%s/', - } - - def __init__(self): - super(Trakt, self).__init__() - - addEvent('setting.save.trakt.automation_password', self.sha1Password) - - def sha1Password(self, value): - return sha1(value) if value else '' - - def getIMDBids(self): - - movies = [] - for movie in self.getWatchlist(): - movies.append(movie.get('imdb_id')) - - return movies - - def getWatchlist(self): - method = (self.urls['watchlist'] % self.conf('automation_api_key')) + self.conf('automation_username') - return self.call(method) - - def call(self, method_url): - - headers = {} - if self.conf('automation_password'): - headers['Authorization'] = 'Basic %s' % base64.encodestring('%s:%s' % (self.conf('automation_username'), self.conf('automation_password')))[:-1] - - data = self.getJsonData(self.urls['base'] + method_url, headers = headers) - return data if data else [] diff --git a/couchpotato/core/media/movie/providers/info/_modifier.py b/couchpotato/core/media/movie/providers/info/_modifier.py new file mode 100644 index 0000000..9e82223 --- /dev/null +++ b/couchpotato/core/media/movie/providers/info/_modifier.py @@ -0,0 +1,119 @@ +from CodernityDB.database import RecordNotFound +from couchpotato import get_db +from couchpotato.core.event import addEvent +from couchpotato.core.helpers.variable import mergeDicts, randomString +from couchpotato.core.logger import CPLog +from couchpotato.core.plugins.base import Plugin +import copy +import traceback + +log = CPLog(__name__) + +autoload = 'MovieResultModifier' + + +class MovieResultModifier(Plugin): + + default_info = { + 'tmdb_id': 0, + 'titles': [], + 'original_title': '', + 'year': 0, + 'images': { + 'poster': [], + 'backdrop': [], + 'poster_original': [], + 'backdrop_original': [], + 'actors': {} + }, + 'runtime': 0, + 'plot': '', + 'tagline': '', + 'imdb': '', + 'genres': [], + 'mpaa': None, + 'actors': [], + 'actor_roles': {} + } + + def __init__(self): + addEvent('result.modify.info.search', self.returnByType) + addEvent('result.modify.movie.search', self.combineOnIMDB) + addEvent('result.modify.movie.info', self.checkLibrary) + + def returnByType(self, results): + + new_results = {} + for r in results: + type_name = r.get('type', 'movie') + 's' + if type_name not in new_results: + new_results[type_name] = [] + + new_results[type_name].append(r) + + # Combine movies, needs a cleaner way.. + if 'movies' in new_results: + new_results['movies'] = self.combineOnIMDB(new_results['movies']) + + return new_results + + def combineOnIMDB(self, results): + + temp = {} + order = [] + + # Combine on imdb id + for item in results: + random_string = randomString() + imdb = item.get('imdb', random_string) + imdb = imdb if imdb else random_string + + if not temp.get(imdb): + temp[imdb] = self.getLibraryTags(imdb) + order.append(imdb) + + # Merge dicts + temp[imdb] = mergeDicts(temp[imdb], item) + + # Make it a list again + temp_list = [temp[x] for x in order] + + return temp_list + + def getLibraryTags(self, imdb): + + temp = { + 'in_wanted': False, + 'in_library': False, + } + + # Add release info from current library + db = get_db() + try: + + media = None + try: + media = db.get('media', imdb, with_doc = True)['doc'] + except RecordNotFound: + pass + + if media: + + if media.get('status') == 'active': + temp['in_wanted'] = media + + for release in db.run('release', 'for_media', media.get('_id')): + if release.get('status') == 'done': + temp['in_library'] = media + except: + log.error('Tried getting more info on searched movies: %s', traceback.format_exc()) + + return temp + + def checkLibrary(self, result): + + result = mergeDicts(copy.deepcopy(self.default_info), copy.deepcopy(result)) + + if result and result.get('imdb'): + return mergeDicts(result, self.getLibraryTags(result['imdb'])) + return result diff --git a/couchpotato/core/media/movie/providers/info/_modifier/__init__.py b/couchpotato/core/media/movie/providers/info/_modifier/__init__.py deleted file mode 100644 index 3bdf5e0..0000000 --- a/couchpotato/core/media/movie/providers/info/_modifier/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .main import MovieResultModifier - -def start(): - - return MovieResultModifier() - -config = [] diff --git a/couchpotato/core/media/movie/providers/info/_modifier/main.py b/couchpotato/core/media/movie/providers/info/_modifier/main.py deleted file mode 100644 index 010f1f5..0000000 --- a/couchpotato/core/media/movie/providers/info/_modifier/main.py +++ /dev/null @@ -1,117 +0,0 @@ -from CodernityDB.database import RecordNotFound -from couchpotato import get_db -from couchpotato.core.event import addEvent -from couchpotato.core.helpers.variable import mergeDicts, randomString -from couchpotato.core.logger import CPLog -from couchpotato.core.plugins.base import Plugin -import copy -import traceback - -log = CPLog(__name__) - - -class MovieResultModifier(Plugin): - - default_info = { - 'tmdb_id': 0, - 'titles': [], - 'original_title': '', - 'year': 0, - 'images': { - 'poster': [], - 'backdrop': [], - 'poster_original': [], - 'backdrop_original': [], - 'actors': {} - }, - 'runtime': 0, - 'plot': '', - 'tagline': '', - 'imdb': '', - 'genres': [], - 'mpaa': None, - 'actors': [], - 'actor_roles': {} - } - - def __init__(self): - addEvent('result.modify.info.search', self.returnByType) - addEvent('result.modify.movie.search', self.combineOnIMDB) - addEvent('result.modify.movie.info', self.checkLibrary) - - def returnByType(self, results): - - new_results = {} - for r in results: - type_name = r.get('type', 'movie') + 's' - if type_name not in new_results: - new_results[type_name] = [] - - new_results[type_name].append(r) - - # Combine movies, needs a cleaner way.. - if 'movies' in new_results: - new_results['movies'] = self.combineOnIMDB(new_results['movies']) - - return new_results - - def combineOnIMDB(self, results): - - temp = {} - order = [] - - # Combine on imdb id - for item in results: - random_string = randomString() - imdb = item.get('imdb', random_string) - imdb = imdb if imdb else random_string - - if not temp.get(imdb): - temp[imdb] = self.getLibraryTags(imdb) - order.append(imdb) - - # Merge dicts - temp[imdb] = mergeDicts(temp[imdb], item) - - # Make it a list again - temp_list = [temp[x] for x in order] - - return temp_list - - def getLibraryTags(self, imdb): - - temp = { - 'in_wanted': False, - 'in_library': False, - } - - # Add release info from current library - db = get_db() - try: - - media = None - try: - media = db.get('media', imdb, with_doc = True)['doc'] - except RecordNotFound: - pass - - if media: - - if media.get('status') == 'active': - temp['in_wanted'] = media - - for release in db.run('release', 'for_media', media.get('_id')): - if release.get('status') == 'done': - temp['in_library'] = media - except: - log.error('Tried getting more info on searched movies: %s', traceback.format_exc()) - - return temp - - def checkLibrary(self, result): - - result = mergeDicts(copy.deepcopy(self.default_info), copy.deepcopy(result)) - - if result and result.get('imdb'): - return mergeDicts(result, self.getLibraryTags(result['imdb'])) - return result diff --git a/couchpotato/core/media/movie/providers/info/couchpotatoapi.py b/couchpotato/core/media/movie/providers/info/couchpotatoapi.py new file mode 100644 index 0000000..272fab9 --- /dev/null +++ b/couchpotato/core/media/movie/providers/info/couchpotatoapi.py @@ -0,0 +1,120 @@ +import base64 +import time + +from couchpotato.core.event import addEvent, fireEvent +from couchpotato.core.helpers.encoding import tryUrlencode +from couchpotato.core.logger import CPLog +from couchpotato.core.media.movie.providers.base import MovieProvider +from couchpotato.environment import Env + + +log = CPLog(__name__) + +autoload = 'CouchPotatoApi' + + +class CouchPotatoApi(MovieProvider): + + urls = { + 'validate': 'https://api.couchpota.to/validate/%s/', + 'search': 'https://api.couchpota.to/search/%s/', + 'info': 'https://api.couchpota.to/info/%s/', + 'is_movie': 'https://api.couchpota.to/ismovie/%s/', + 'eta': 'https://api.couchpota.to/eta/%s/', + 'suggest': 'https://api.couchpota.to/suggest/', + 'updater': 'https://api.couchpota.to/updater/?%s', + 'messages': 'https://api.couchpota.to/messages/?%s', + } + http_time_between_calls = 0 + api_version = 1 + + def __init__(self): + addEvent('movie.info', self.getInfo, priority = 1) + addEvent('movie.info.release_date', self.getReleaseDate) + + addEvent('info.search', self.search, priority = 1) + addEvent('movie.search', self.search, priority = 1) + + addEvent('movie.suggest', self.getSuggestions) + addEvent('movie.is_movie', self.isMovie) + + addEvent('release.validate', self.validate) + + addEvent('cp.source_url', self.getSourceUrl) + addEvent('cp.messages', self.getMessages) + + def getMessages(self, last_check = 0): + + data = self.getJsonData(self.urls['messages'] % tryUrlencode({ + 'last_check': last_check, + }), headers = self.getRequestHeaders(), cache_timeout = 10) + + return data + + def getSourceUrl(self, repo = None, repo_name = None, branch = None): + return self.getJsonData(self.urls['updater'] % tryUrlencode({ + 'repo': repo, + 'name': repo_name, + 'branch': branch, + }), headers = self.getRequestHeaders()) + + def search(self, q, limit = 5): + return self.getJsonData(self.urls['search'] % tryUrlencode(q) + ('?limit=%s' % limit), headers = self.getRequestHeaders()) + + def validate(self, name = None): + + if not name: + return + + name_enc = base64.b64encode(name) + return self.getJsonData(self.urls['validate'] % name_enc, headers = self.getRequestHeaders()) + + def isMovie(self, identifier = None): + + if not identifier: + return + + data = self.getJsonData(self.urls['is_movie'] % identifier, headers = self.getRequestHeaders()) + if data: + return data.get('is_movie', True) + + return True + + def getInfo(self, identifier = None, **kwargs): + + if not identifier: + return + + result = self.getJsonData(self.urls['info'] % identifier, headers = self.getRequestHeaders()) + if result: + return dict((k, v) for k, v in result.items() if v) + + return {} + + def getReleaseDate(self, identifier = None): + if identifier is None: return {} + + dates = self.getJsonData(self.urls['eta'] % identifier, headers = self.getRequestHeaders()) + log.debug('Found ETA for %s: %s', (identifier, dates)) + + return dates + + def getSuggestions(self, movies = None, ignore = None): + if not ignore: ignore = [] + if not movies: movies = [] + + suggestions = self.getJsonData(self.urls['suggest'], data = { + 'movies': ','.join(movies), + 'ignore': ','.join(ignore), + }, headers = self.getRequestHeaders()) + log.info('Found suggestions for %s movies, %s ignored', (len(movies), len(ignore))) + + return suggestions + + def getRequestHeaders(self): + return { + 'X-CP-Version': fireEvent('app.version', single = True), + 'X-CP-API': self.api_version, + 'X-CP-Time': time.time(), + 'X-CP-Identifier': '+%s' % Env.setting('api_key', 'core')[:10], # Use first 10 as identifier, so we don't need to use IP address in api stats + } diff --git a/couchpotato/core/media/movie/providers/info/couchpotatoapi/__init__.py b/couchpotato/core/media/movie/providers/info/couchpotatoapi/__init__.py deleted file mode 100644 index 196dde6..0000000 --- a/couchpotato/core/media/movie/providers/info/couchpotatoapi/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .main import CouchPotatoApi - - -def start(): - return CouchPotatoApi() - -config = [] diff --git a/couchpotato/core/media/movie/providers/info/couchpotatoapi/main.py b/couchpotato/core/media/movie/providers/info/couchpotatoapi/main.py deleted file mode 100644 index 469866c..0000000 --- a/couchpotato/core/media/movie/providers/info/couchpotatoapi/main.py +++ /dev/null @@ -1,118 +0,0 @@ -import base64 -import time - -from couchpotato.core.event import addEvent, fireEvent -from couchpotato.core.helpers.encoding import tryUrlencode -from couchpotato.core.logger import CPLog -from couchpotato.core.media.movie.providers.base import MovieProvider -from couchpotato.environment import Env - - -log = CPLog(__name__) - - -class CouchPotatoApi(MovieProvider): - - urls = { - 'validate': 'https://api.couchpota.to/validate/%s/', - 'search': 'https://api.couchpota.to/search/%s/', - 'info': 'https://api.couchpota.to/info/%s/', - 'is_movie': 'https://api.couchpota.to/ismovie/%s/', - 'eta': 'https://api.couchpota.to/eta/%s/', - 'suggest': 'https://api.couchpota.to/suggest/', - 'updater': 'https://api.couchpota.to/updater/?%s', - 'messages': 'https://api.couchpota.to/messages/?%s', - } - http_time_between_calls = 0 - api_version = 1 - - def __init__(self): - addEvent('movie.info', self.getInfo, priority = 1) - addEvent('movie.info.release_date', self.getReleaseDate) - - addEvent('info.search', self.search, priority = 1) - addEvent('movie.search', self.search, priority = 1) - - addEvent('movie.suggest', self.getSuggestions) - addEvent('movie.is_movie', self.isMovie) - - addEvent('release.validate', self.validate) - - addEvent('cp.source_url', self.getSourceUrl) - addEvent('cp.messages', self.getMessages) - - def getMessages(self, last_check = 0): - - data = self.getJsonData(self.urls['messages'] % tryUrlencode({ - 'last_check': last_check, - }), headers = self.getRequestHeaders(), cache_timeout = 10) - - return data - - def getSourceUrl(self, repo = None, repo_name = None, branch = None): - return self.getJsonData(self.urls['updater'] % tryUrlencode({ - 'repo': repo, - 'name': repo_name, - 'branch': branch, - }), headers = self.getRequestHeaders()) - - def search(self, q, limit = 5): - return self.getJsonData(self.urls['search'] % tryUrlencode(q) + ('?limit=%s' % limit), headers = self.getRequestHeaders()) - - def validate(self, name = None): - - if not name: - return - - name_enc = base64.b64encode(name) - return self.getJsonData(self.urls['validate'] % name_enc, headers = self.getRequestHeaders()) - - def isMovie(self, identifier = None): - - if not identifier: - return - - data = self.getJsonData(self.urls['is_movie'] % identifier, headers = self.getRequestHeaders()) - if data: - return data.get('is_movie', True) - - return True - - def getInfo(self, identifier = None, **kwargs): - - if not identifier: - return - - result = self.getJsonData(self.urls['info'] % identifier, headers = self.getRequestHeaders()) - if result: - return dict((k, v) for k, v in result.items() if v) - - return {} - - def getReleaseDate(self, identifier = None): - if identifier is None: return {} - - dates = self.getJsonData(self.urls['eta'] % identifier, headers = self.getRequestHeaders()) - log.debug('Found ETA for %s: %s', (identifier, dates)) - - return dates - - def getSuggestions(self, movies = None, ignore = None): - if not ignore: ignore = [] - if not movies: movies = [] - - suggestions = self.getJsonData(self.urls['suggest'], data = { - 'movies': ','.join(movies), - 'ignore': ','.join(ignore), - }, headers = self.getRequestHeaders()) - log.info('Found suggestions for %s movies, %s ignored', (len(movies), len(ignore))) - - return suggestions - - def getRequestHeaders(self): - return { - 'X-CP-Version': fireEvent('app.version', single = True), - 'X-CP-API': self.api_version, - 'X-CP-Time': time.time(), - 'X-CP-Identifier': '+%s' % Env.setting('api_key', 'core')[:10], # Use first 10 as identifier, so we don't need to use IP address in api stats - } diff --git a/couchpotato/core/media/movie/providers/info/omdbapi.py b/couchpotato/core/media/movie/providers/info/omdbapi.py new file mode 100644 index 0000000..342df3c --- /dev/null +++ b/couchpotato/core/media/movie/providers/info/omdbapi.py @@ -0,0 +1,129 @@ +import json +import re +import traceback + +from couchpotato.core.event import addEvent, fireEvent +from couchpotato.core.helpers.encoding import tryUrlencode +from couchpotato.core.helpers.variable import tryInt, tryFloat, splitString +from couchpotato.core.logger import CPLog +from couchpotato.core.media.movie.providers.base import MovieProvider + + +log = CPLog(__name__) + +autoload = 'OMDBAPI' + + +class OMDBAPI(MovieProvider): + + urls = { + 'search': 'http://www.omdbapi.com/?%s', + 'info': 'http://www.omdbapi.com/?i=%s', + } + + http_time_between_calls = 0 + + def __init__(self): + addEvent('info.search', self.search) + addEvent('movie.search', self.search) + addEvent('movie.info', self.getInfo) + + def search(self, q, limit = 12): + + name_year = fireEvent('scanner.name_year', q, single = True) + + if not name_year or (name_year and not name_year.get('name')): + name_year = { + 'name': q + } + + cache_key = 'omdbapi.cache.%s' % q + cached = self.getCache(cache_key, self.urls['search'] % tryUrlencode({'t': name_year.get('name'), 'y': name_year.get('year', '')}), timeout = 3) + + if cached: + result = self.parseMovie(cached) + if result.get('titles') and len(result.get('titles')) > 0: + log.info('Found: %s', result['titles'][0] + ' (' + str(result.get('year')) + ')') + return [result] + + return [] + + return [] + + def getInfo(self, identifier = None, **kwargs): + + if not identifier: + return {} + + cache_key = 'omdbapi.cache.%s' % identifier + cached = self.getCache(cache_key, self.urls['info'] % identifier, timeout = 3) + + if cached: + result = self.parseMovie(cached) + if result.get('titles') and len(result.get('titles')) > 0: + log.info('Found: %s', result['titles'][0] + ' (' + str(result['year']) + ')') + return result + + return {} + + def parseMovie(self, movie): + + movie_data = {} + try: + + try: + if isinstance(movie, (str, unicode)): + movie = json.loads(movie) + except ValueError: + log.info('No proper json to decode') + return movie_data + + if movie.get('Response') == 'Parse Error' or movie.get('Response') == 'False': + return movie_data + + tmp_movie = movie.copy() + for key in tmp_movie: + if tmp_movie.get(key).lower() == 'n/a': + del movie[key] + + year = tryInt(movie.get('Year', '')) + + movie_data = { + 'type': 'movie', + 'via_imdb': True, + 'titles': [movie.get('Title')] if movie.get('Title') else [], + 'original_title': movie.get('Title'), + 'images': { + 'poster': [movie.get('Poster', '')] if movie.get('Poster') and len(movie.get('Poster', '')) > 4 else [], + }, + 'rating': { + 'imdb': (tryFloat(movie.get('imdbRating', 0)), tryInt(movie.get('imdbVotes', '').replace(',', ''))), + #'rotten': (tryFloat(movie.get('tomatoRating', 0)), tryInt(movie.get('tomatoReviews', '').replace(',', ''))), + }, + 'imdb': str(movie.get('imdbID', '')), + 'mpaa': str(movie.get('Rated', '')), + 'runtime': self.runtimeToMinutes(movie.get('Runtime', '')), + 'released': movie.get('Released'), + 'year': year if isinstance(year, int) else None, + 'plot': movie.get('Plot'), + 'genres': splitString(movie.get('Genre', '')), + 'directors': splitString(movie.get('Director', '')), + 'writers': splitString(movie.get('Writer', '')), + 'actors': splitString(movie.get('Actors', '')), + } + movie_data = dict((k, v) for k, v in movie_data.items() if v) + except: + log.error('Failed parsing IMDB API json: %s', traceback.format_exc()) + + return movie_data + + def runtimeToMinutes(self, runtime_str): + runtime = 0 + + regex = '(\d*.?\d+).(h|hr|hrs|mins|min)+' + matches = re.findall(regex, runtime_str) + for match in matches: + nr, size = match + runtime += tryInt(nr) * (60 if 'h' is str(size)[0] else 1) + + return runtime diff --git a/couchpotato/core/media/movie/providers/info/omdbapi/__init__.py b/couchpotato/core/media/movie/providers/info/omdbapi/__init__.py deleted file mode 100644 index b7ea393..0000000 --- a/couchpotato/core/media/movie/providers/info/omdbapi/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .main import OMDBAPI - - -def start(): - return OMDBAPI() - -config = [] diff --git a/couchpotato/core/media/movie/providers/info/omdbapi/main.py b/couchpotato/core/media/movie/providers/info/omdbapi/main.py deleted file mode 100644 index 34e7d56..0000000 --- a/couchpotato/core/media/movie/providers/info/omdbapi/main.py +++ /dev/null @@ -1,127 +0,0 @@ -import json -import re -import traceback - -from couchpotato.core.event import addEvent, fireEvent -from couchpotato.core.helpers.encoding import tryUrlencode -from couchpotato.core.helpers.variable import tryInt, tryFloat, splitString -from couchpotato.core.logger import CPLog -from couchpotato.core.media.movie.providers.base import MovieProvider - - -log = CPLog(__name__) - - -class OMDBAPI(MovieProvider): - - urls = { - 'search': 'http://www.omdbapi.com/?%s', - 'info': 'http://www.omdbapi.com/?i=%s', - } - - http_time_between_calls = 0 - - def __init__(self): - addEvent('info.search', self.search) - addEvent('movie.search', self.search) - addEvent('movie.info', self.getInfo) - - def search(self, q, limit = 12): - - name_year = fireEvent('scanner.name_year', q, single = True) - - if not name_year or (name_year and not name_year.get('name')): - name_year = { - 'name': q - } - - cache_key = 'omdbapi.cache.%s' % q - cached = self.getCache(cache_key, self.urls['search'] % tryUrlencode({'t': name_year.get('name'), 'y': name_year.get('year', '')}), timeout = 3) - - if cached: - result = self.parseMovie(cached) - if result.get('titles') and len(result.get('titles')) > 0: - log.info('Found: %s', result['titles'][0] + ' (' + str(result.get('year')) + ')') - return [result] - - return [] - - return [] - - def getInfo(self, identifier = None, **kwargs): - - if not identifier: - return {} - - cache_key = 'omdbapi.cache.%s' % identifier - cached = self.getCache(cache_key, self.urls['info'] % identifier, timeout = 3) - - if cached: - result = self.parseMovie(cached) - if result.get('titles') and len(result.get('titles')) > 0: - log.info('Found: %s', result['titles'][0] + ' (' + str(result['year']) + ')') - return result - - return {} - - def parseMovie(self, movie): - - movie_data = {} - try: - - try: - if isinstance(movie, (str, unicode)): - movie = json.loads(movie) - except ValueError: - log.info('No proper json to decode') - return movie_data - - if movie.get('Response') == 'Parse Error' or movie.get('Response') == 'False': - return movie_data - - tmp_movie = movie.copy() - for key in tmp_movie: - if tmp_movie.get(key).lower() == 'n/a': - del movie[key] - - year = tryInt(movie.get('Year', '')) - - movie_data = { - 'type': 'movie', - 'via_imdb': True, - 'titles': [movie.get('Title')] if movie.get('Title') else [], - 'original_title': movie.get('Title'), - 'images': { - 'poster': [movie.get('Poster', '')] if movie.get('Poster') and len(movie.get('Poster', '')) > 4 else [], - }, - 'rating': { - 'imdb': (tryFloat(movie.get('imdbRating', 0)), tryInt(movie.get('imdbVotes', '').replace(',', ''))), - #'rotten': (tryFloat(movie.get('tomatoRating', 0)), tryInt(movie.get('tomatoReviews', '').replace(',', ''))), - }, - 'imdb': str(movie.get('imdbID', '')), - 'mpaa': str(movie.get('Rated', '')), - 'runtime': self.runtimeToMinutes(movie.get('Runtime', '')), - 'released': movie.get('Released'), - 'year': year if isinstance(year, int) else None, - 'plot': movie.get('Plot'), - 'genres': splitString(movie.get('Genre', '')), - 'directors': splitString(movie.get('Director', '')), - 'writers': splitString(movie.get('Writer', '')), - 'actors': splitString(movie.get('Actors', '')), - } - movie_data = dict((k, v) for k, v in movie_data.items() if v) - except: - log.error('Failed parsing IMDB API json: %s', traceback.format_exc()) - - return movie_data - - def runtimeToMinutes(self, runtime_str): - runtime = 0 - - regex = '(\d*.?\d+).(h|hr|hrs|mins|min)+' - matches = re.findall(regex, runtime_str) - for match in matches: - nr, size = match - runtime += tryInt(nr) * (60 if 'h' is str(size)[0] else 1) - - return runtime diff --git a/couchpotato/core/media/movie/providers/info/themoviedb.py b/couchpotato/core/media/movie/providers/info/themoviedb.py new file mode 100644 index 0000000..9f5c5e8 --- /dev/null +++ b/couchpotato/core/media/movie/providers/info/themoviedb.py @@ -0,0 +1,199 @@ +import traceback + +from couchpotato.core.event import addEvent +from couchpotato.core.helpers.encoding import simplifyString, toUnicode, ss +from couchpotato.core.helpers.variable import tryInt +from couchpotato.core.logger import CPLog +from couchpotato.core.media.movie.providers.base import MovieProvider +import tmdb3 + +log = CPLog(__name__) + +autoload = 'TheMovieDb' + + +class TheMovieDb(MovieProvider): + + def __init__(self): + addEvent('movie.info', self.getInfo, priority = 2) + addEvent('movie.info_by_tmdb', self.getInfo) + + # Configure TMDB settings + tmdb3.set_key(self.conf('api_key')) + tmdb3.set_cache('null') + + def search(self, q, limit = 12): + """ Find movie by name """ + + if self.isDisabled(): + return False + + search_string = simplifyString(q) + cache_key = 'tmdb.cache.%s.%s' % (search_string, limit) + results = self.getCache(cache_key) + + if not results: + log.debug('Searching for movie: %s', q) + + raw = None + try: + raw = tmdb3.searchMovie(search_string) + except: + log.error('Failed searching TMDB for "%s": %s', (search_string, traceback.format_exc())) + + results = [] + if raw: + try: + nr = 0 + + for movie in raw: + results.append(self.parseMovie(movie, extended = False)) + + nr += 1 + if nr == limit: + break + + log.info('Found: %s', [result['titles'][0] + ' (' + str(result.get('year', 0)) + ')' for result in results]) + + self.setCache(cache_key, results) + return results + except SyntaxError as e: + log.error('Failed to parse XML response: %s', e) + return False + + return results + + def getInfo(self, identifier = None, extended = True): + + if not identifier: + return {} + + cache_key = 'tmdb.cache.%s%s' % (identifier, '.ex' if extended else '') + result = self.getCache(cache_key) + + if not result: + try: + log.debug('Getting info: %s', cache_key) + movie = tmdb3.Movie(identifier) + try: exists = movie.title is not None + except: exists = False + + if exists: + result = self.parseMovie(movie, extended = extended) + self.setCache(cache_key, result) + else: + result = {} + except: + log.error('Failed getting info for %s: %s', (identifier, traceback.format_exc())) + + return result + + def parseMovie(self, movie, extended = True): + + cache_key = 'tmdb.cache.%s%s' % (movie.id, '.ex' if extended else '') + movie_data = self.getCache(cache_key) + + if not movie_data: + + # Images + poster = self.getImage(movie, type = 'poster', size = 'poster') + poster_original = self.getImage(movie, type = 'poster', size = 'original') + backdrop_original = self.getImage(movie, type = 'backdrop', size = 'original') + + images = { + 'poster': [poster] if poster else [], + #'backdrop': [backdrop] if backdrop else [], + 'poster_original': [poster_original] if poster_original else [], + 'backdrop_original': [backdrop_original] if backdrop_original else [], + 'actors': {} + } + + # Genres + try: + genres = [genre.name for genre in movie.genres] + except: + genres = [] + + # 1900 is the same as None + year = str(movie.releasedate or '')[:4] + if not movie.releasedate or year == '1900' or year.lower() == 'none': + year = None + + # Gather actors data + actors = {} + if extended: + for cast_item in movie.cast: + try: + actors[toUnicode(cast_item.name)] = toUnicode(cast_item.character) + images['actors'][toUnicode(cast_item.name)] = self.getImage(cast_item, type = 'profile', size = 'original') + except: + log.debug('Error getting cast info for %s: %s', (cast_item, traceback.format_exc())) + + movie_data = { + 'type': 'movie', + 'via_tmdb': True, + 'tmdb_id': movie.id, + 'titles': [toUnicode(movie.title)], + 'original_title': movie.originaltitle, + 'images': images, + 'imdb': movie.imdb, + 'runtime': movie.runtime, + 'released': str(movie.releasedate), + 'year': tryInt(year, None), + 'plot': movie.overview, + 'genres': genres, + 'collection': getattr(movie.collection, 'name', None), + 'actor_roles': actors + } + + movie_data = dict((k, v) for k, v in movie_data.items() if v) + + # Add alternative names + if extended: + movie_data['titles'].append(movie.originaltitle) + for alt in movie.alternate_titles: + alt_name = alt.title + if alt_name and alt_name not in movie_data['titles'] and alt_name.lower() != 'none' and alt_name is not None: + movie_data['titles'].append(alt_name) + + # Cache movie parsed + self.setCache(cache_key, movie_data) + + return movie_data + + def getImage(self, movie, type = 'poster', size = 'poster'): + + image_url = '' + try: + image_url = getattr(movie, type).geturl(size = size) + except: + log.debug('Failed getting %s.%s for "%s"', (type, size, ss(str(movie)))) + + return image_url + + def isDisabled(self): + if self.conf('api_key') == '': + log.error('No API key provided.') + return True + return False + + +config = [{ + 'name': 'themoviedb', + 'groups': [ + { + 'tab': 'providers', + 'name': 'tmdb', + 'label': 'TheMovieDB', + 'hidden': True, + 'description': 'Used for all calls to TheMovieDB.', + 'options': [ + { + 'name': 'api_key', + 'default': '9b939aee0aaafc12a65bf448e4af9543', + 'label': 'Api Key', + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/providers/info/themoviedb/__init__.py b/couchpotato/core/media/movie/providers/info/themoviedb/__init__.py deleted file mode 100644 index b981950..0000000 --- a/couchpotato/core/media/movie/providers/info/themoviedb/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -from .main import TheMovieDb - - -def start(): - return TheMovieDb() - -config = [{ - 'name': 'themoviedb', - 'groups': [ - { - 'tab': 'providers', - 'name': 'tmdb', - 'label': 'TheMovieDB', - 'hidden': True, - 'description': 'Used for all calls to TheMovieDB.', - 'options': [ - { - 'name': 'api_key', - 'default': '9b939aee0aaafc12a65bf448e4af9543', - 'label': 'Api Key', - }, - ], - }, - ], -}] diff --git a/couchpotato/core/media/movie/providers/info/themoviedb/main.py b/couchpotato/core/media/movie/providers/info/themoviedb/main.py deleted file mode 100644 index 36f3a48..0000000 --- a/couchpotato/core/media/movie/providers/info/themoviedb/main.py +++ /dev/null @@ -1,177 +0,0 @@ -import traceback - -from couchpotato.core.event import addEvent -from couchpotato.core.helpers.encoding import simplifyString, toUnicode, ss -from couchpotato.core.helpers.variable import tryInt -from couchpotato.core.logger import CPLog -from couchpotato.core.media.movie.providers.base import MovieProvider -import tmdb3 - - -log = CPLog(__name__) - - -class TheMovieDb(MovieProvider): - - def __init__(self): - addEvent('movie.info', self.getInfo, priority = 2) - addEvent('movie.info_by_tmdb', self.getInfo) - - # Configure TMDB settings - tmdb3.set_key(self.conf('api_key')) - tmdb3.set_cache('null') - - def search(self, q, limit = 12): - """ Find movie by name """ - - if self.isDisabled(): - return False - - search_string = simplifyString(q) - cache_key = 'tmdb.cache.%s.%s' % (search_string, limit) - results = self.getCache(cache_key) - - if not results: - log.debug('Searching for movie: %s', q) - - raw = None - try: - raw = tmdb3.searchMovie(search_string) - except: - log.error('Failed searching TMDB for "%s": %s', (search_string, traceback.format_exc())) - - results = [] - if raw: - try: - nr = 0 - - for movie in raw: - results.append(self.parseMovie(movie, extended = False)) - - nr += 1 - if nr == limit: - break - - log.info('Found: %s', [result['titles'][0] + ' (' + str(result.get('year', 0)) + ')' for result in results]) - - self.setCache(cache_key, results) - return results - except SyntaxError as e: - log.error('Failed to parse XML response: %s', e) - return False - - return results - - def getInfo(self, identifier = None, extended = True): - - if not identifier: - return {} - - cache_key = 'tmdb.cache.%s%s' % (identifier, '.ex' if extended else '') - result = self.getCache(cache_key) - - if not result: - try: - log.debug('Getting info: %s', cache_key) - movie = tmdb3.Movie(identifier) - try: exists = movie.title is not None - except: exists = False - - if exists: - result = self.parseMovie(movie, extended = extended) - self.setCache(cache_key, result) - else: - result = {} - except: - log.error('Failed getting info for %s: %s', (identifier, traceback.format_exc())) - - return result - - def parseMovie(self, movie, extended = True): - - cache_key = 'tmdb.cache.%s%s' % (movie.id, '.ex' if extended else '') - movie_data = self.getCache(cache_key) - - if not movie_data: - - # Images - poster = self.getImage(movie, type = 'poster', size = 'poster') - poster_original = self.getImage(movie, type = 'poster', size = 'original') - backdrop_original = self.getImage(movie, type = 'backdrop', size = 'original') - - images = { - 'poster': [poster] if poster else [], - #'backdrop': [backdrop] if backdrop else [], - 'poster_original': [poster_original] if poster_original else [], - 'backdrop_original': [backdrop_original] if backdrop_original else [], - 'actors': {} - } - - # Genres - try: - genres = [genre.name for genre in movie.genres] - except: - genres = [] - - # 1900 is the same as None - year = str(movie.releasedate or '')[:4] - if not movie.releasedate or year == '1900' or year.lower() == 'none': - year = None - - # Gather actors data - actors = {} - if extended: - for cast_item in movie.cast: - try: - actors[toUnicode(cast_item.name)] = toUnicode(cast_item.character) - images['actors'][toUnicode(cast_item.name)] = self.getImage(cast_item, type = 'profile', size = 'original') - except: - log.debug('Error getting cast info for %s: %s', (cast_item, traceback.format_exc())) - - movie_data = { - 'type': 'movie', - 'via_tmdb': True, - 'tmdb_id': movie.id, - 'titles': [toUnicode(movie.title)], - 'original_title': movie.originaltitle, - 'images': images, - 'imdb': movie.imdb, - 'runtime': movie.runtime, - 'released': str(movie.releasedate), - 'year': tryInt(year, None), - 'plot': movie.overview, - 'genres': genres, - 'collection': getattr(movie.collection, 'name', None), - 'actor_roles': actors - } - - movie_data = dict((k, v) for k, v in movie_data.items() if v) - - # Add alternative names - if extended: - movie_data['titles'].append(movie.originaltitle) - for alt in movie.alternate_titles: - alt_name = alt.title - if alt_name and alt_name not in movie_data['titles'] and alt_name.lower() != 'none' and alt_name is not None: - movie_data['titles'].append(alt_name) - - # Cache movie parsed - self.setCache(cache_key, movie_data) - - return movie_data - - def getImage(self, movie, type = 'poster', size = 'poster'): - - image_url = '' - try: - image_url = getattr(movie, type).geturl(size = size) - except: - log.debug('Failed getting %s.%s for "%s"', (type, size, ss(str(movie)))) - - return image_url - - def isDisabled(self): - if self.conf('api_key') == '': - log.error('No API key provided.') - return True - return False diff --git a/couchpotato/core/media/movie/providers/metadata/wmc.py b/couchpotato/core/media/movie/providers/metadata/wmc.py new file mode 100644 index 0000000..50db02d --- /dev/null +++ b/couchpotato/core/media/movie/providers/metadata/wmc.py @@ -0,0 +1,31 @@ +from couchpotato.core.media.movie.providers.metadata.base import MovieMetaData +import os + +autoload = 'WindowsMediaCenter' + + +class WindowsMediaCenter(MovieMetaData): + + def getThumbnailName(self, name, root): + return os.path.join(root, 'folder.jpg') + + +config = [{ + 'name': 'windowsmediacenter', + 'groups': [ + { + 'tab': 'renamer', + 'subtab': 'metadata', + 'name': 'windowsmediacenter_metadata', + 'label': 'Windows Explorer / Media Center', + 'description': 'Generate folder.jpg', + 'options': [ + { + 'name': 'meta_enabled', + 'default': False, + 'type': 'enabler', + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/providers/metadata/wmc/__init__.py b/couchpotato/core/media/movie/providers/metadata/wmc/__init__.py deleted file mode 100644 index 167a24d..0000000 --- a/couchpotato/core/media/movie/providers/metadata/wmc/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -from .main import WindowsMediaCenter - - -def start(): - return WindowsMediaCenter() - -config = [{ - 'name': 'windowsmediacenter', - 'groups': [ - { - 'tab': 'renamer', - 'subtab': 'metadata', - 'name': 'windowsmediacenter_metadata', - 'label': 'Windows Explorer / Media Center', - 'description': 'Generate folder.jpg', - 'options': [ - { - 'name': 'meta_enabled', - 'default': False, - 'type': 'enabler', - }, - ], - }, - ], -}] diff --git a/couchpotato/core/media/movie/providers/metadata/wmc/main.py b/couchpotato/core/media/movie/providers/metadata/wmc/main.py deleted file mode 100644 index bdb96a4..0000000 --- a/couchpotato/core/media/movie/providers/metadata/wmc/main.py +++ /dev/null @@ -1,8 +0,0 @@ -from couchpotato.core.media.movie.providers.metadata.base import MovieMetaData -import os - - -class WindowsMediaCenter(MovieMetaData): - - def getThumbnailName(self, name, root): - return os.path.join(root, 'folder.jpg') diff --git a/couchpotato/core/media/movie/providers/metadata/xbmc.py b/couchpotato/core/media/movie/providers/metadata/xbmc.py new file mode 100644 index 0000000..e9904cf --- /dev/null +++ b/couchpotato/core/media/movie/providers/metadata/xbmc.py @@ -0,0 +1,243 @@ +from couchpotato.core.media.movie.providers.metadata.base import MovieMetaData +from couchpotato.core.helpers.encoding import toUnicode +from couchpotato.core.helpers.variable import getTitle +from couchpotato.core.logger import CPLog +from xml.etree.ElementTree import Element, SubElement, tostring +import os +import re +import traceback +import xml.dom.minidom + +log = CPLog(__name__) + +autoload = 'XBMC' + + +class XBMC(MovieMetaData): + + def getFanartName(self, name, root): + return self.createMetaName(self.conf('meta_fanart_name'), name, root) + + def getThumbnailName(self, name, root): + return self.createMetaName(self.conf('meta_thumbnail_name'), name, root) + + def getNfoName(self, name, root): + return self.createMetaName(self.conf('meta_nfo_name'), name, root) + + def createMetaName(self, basename, name, root): + return os.path.join(root, basename.replace('%s', name)) + + def getNfo(self, movie_info = None, data = None): + if not data: data = {} + if not movie_info: movie_info = {} + + # return imdb url only + if self.conf('meta_url_only'): + return 'http://www.imdb.com/title/%s/' % toUnicode(data['identifier']) + + nfoxml = Element('movie') + + # Title + try: + el = SubElement(nfoxml, 'title') + el.text = toUnicode(getTitle(data)) + except: + pass + + # IMDB id + try: + el = SubElement(nfoxml, 'id') + el.text = toUnicode(data['identifier']) + except: + pass + + # Runtime + try: + runtime = SubElement(nfoxml, 'runtime') + runtime.text = '%s min' % movie_info.get('runtime') + except: + pass + + # Other values + types = ['year', 'mpaa', 'originaltitle:original_title', 'outline', 'plot', 'tagline', 'premiered:released'] + for type in types: + + if ':' in type: + name, type = type.split(':') + else: + name = type + + try: + if movie_info.get(type): + el = SubElement(nfoxml, name) + el.text = toUnicode(movie_info.get(type, '')) + except: + pass + + # Rating + for rating_type in ['imdb', 'rotten', 'tmdb']: + try: + r, v = movie_info['rating'][rating_type] + rating = SubElement(nfoxml, 'rating') + rating.text = str(r) + votes = SubElement(nfoxml, 'votes') + votes.text = str(v) + break + except: + log.debug('Failed adding rating info from %s: %s', (rating_type, traceback.format_exc())) + + # Genre + for genre in movie_info.get('genres', []): + genres = SubElement(nfoxml, 'genre') + genres.text = toUnicode(genre) + + # Actors + for actor_name in movie_info.get('actor_roles', {}): + role_name = movie_info['actor_roles'][actor_name] + + actor = SubElement(nfoxml, 'actor') + name = SubElement(actor, 'name') + name.text = toUnicode(actor_name) + if role_name: + role = SubElement(actor, 'role') + role.text = toUnicode(role_name) + if movie_info['images']['actors'].get(actor_name): + thumb = SubElement(actor, 'thumb') + thumb.text = toUnicode(movie_info['images']['actors'].get(actor_name)) + + # Directors + for director_name in movie_info.get('directors', []): + director = SubElement(nfoxml, 'director') + director.text = toUnicode(director_name) + + # Writers + for writer in movie_info.get('writers', []): + writers = SubElement(nfoxml, 'credits') + writers.text = toUnicode(writer) + + # Sets or collections + collection_name = movie_info.get('collection') + if collection_name: + collection = SubElement(nfoxml, 'set') + collection.text = toUnicode(collection_name) + sorttitle = SubElement(nfoxml, 'sorttitle') + sorttitle.text = '%s %s' % (toUnicode(collection_name), movie_info.get('year')) + + # Images + for image_url in movie_info['images']['poster_original']: + image = SubElement(nfoxml, 'thumb') + image.text = toUnicode(image_url) + fanart = SubElement(nfoxml, 'fanart') + for image_url in movie_info['images']['backdrop_original']: + image = SubElement(fanart, 'thumb') + image.text = toUnicode(image_url) + + # Add trailer if found + trailer_found = False + if data.get('renamed_files'): + for filename in data.get('renamed_files'): + if 'trailer' in filename: + trailer = SubElement(nfoxml, 'trailer') + trailer.text = toUnicode(filename) + trailer_found = True + if not trailer_found and data['files'].get('trailer'): + trailer = SubElement(nfoxml, 'trailer') + trailer.text = toUnicode(data['files']['trailer'][0]) + + # Add file metadata + fileinfo = SubElement(nfoxml, 'fileinfo') + streamdetails = SubElement(fileinfo, 'streamdetails') + + # Video data + if data['meta_data'].get('video'): + video = SubElement(streamdetails, 'video') + codec = SubElement(video, 'codec') + codec.text = toUnicode(data['meta_data']['video']) + aspect = SubElement(video, 'aspect') + aspect.text = str(data['meta_data']['aspect']) + width = SubElement(video, 'width') + width.text = str(data['meta_data']['resolution_width']) + height = SubElement(video, 'height') + height.text = str(data['meta_data']['resolution_height']) + + # Audio data + if data['meta_data'].get('audio'): + audio = SubElement(streamdetails, 'audio') + codec = SubElement(audio, 'codec') + codec.text = toUnicode(data['meta_data'].get('audio')) + channels = SubElement(audio, 'channels') + channels.text = toUnicode(data['meta_data'].get('audio_channels')) + + # Clean up the xml and return it + nfoxml = xml.dom.minidom.parseString(tostring(nfoxml)) + xml_string = nfoxml.toprettyxml(indent = ' ') + text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+\g<1>%s is the rootname of the movie. For example "/path/to/movie cd1.mkv" will be "/path/to/movie"' + }, + { + 'name': 'meta_url_only', + 'label': 'Only IMDB URL', + 'default': False, + 'advanced': True, + 'description': 'Create a nfo with only the IMDB url inside', + 'type': 'bool', + }, + { + 'name': 'meta_fanart', + 'label': 'Fanart', + 'default': True, + 'type': 'bool', + }, + { + 'name': 'meta_fanart_name', + 'label': 'Fanart filename', + 'default': '%s-fanart.jpg', + 'advanced': True, + }, + { + 'name': 'meta_thumbnail', + 'label': 'Thumbnail', + 'default': True, + 'type': 'bool', + }, + { + 'name': 'meta_thumbnail_name', + 'label': 'Thumbnail filename', + 'default': '%s.tbn', + 'advanced': True, + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/providers/metadata/xbmc/__init__.py b/couchpotato/core/media/movie/providers/metadata/xbmc/__init__.py deleted file mode 100644 index deb5c90..0000000 --- a/couchpotato/core/media/movie/providers/metadata/xbmc/__init__.py +++ /dev/null @@ -1,70 +0,0 @@ -from .main import XBMC - - -def start(): - return XBMC() - -config = [{ - 'name': 'xbmc', - 'groups': [ - { - 'tab': 'renamer', - 'subtab': 'metadata', - 'name': 'xbmc_metadata', - 'label': 'XBMC', - 'description': 'Enable metadata XBMC can understand', - 'options': [ - { - 'name': 'meta_enabled', - 'default': False, - 'type': 'enabler', - }, - { - 'name': 'meta_nfo', - 'label': 'NFO', - 'default': True, - 'type': 'bool', - }, - { - 'name': 'meta_nfo_name', - 'label': 'NFO filename', - 'default': '%s.nfo', - 'advanced': True, - 'description': '%s is the rootname of the movie. For example "/path/to/movie cd1.mkv" will be "/path/to/movie"' - }, - { - 'name': 'meta_url_only', - 'label': 'Only IMDB URL', - 'default': False, - 'advanced': True, - 'description': 'Create a nfo with only the IMDB url inside', - 'type': 'bool', - }, - { - 'name': 'meta_fanart', - 'label': 'Fanart', - 'default': True, - 'type': 'bool', - }, - { - 'name': 'meta_fanart_name', - 'label': 'Fanart filename', - 'default': '%s-fanart.jpg', - 'advanced': True, - }, - { - 'name': 'meta_thumbnail', - 'label': 'Thumbnail', - 'default': True, - 'type': 'bool', - }, - { - 'name': 'meta_thumbnail_name', - 'label': 'Thumbnail filename', - 'default': '%s.tbn', - 'advanced': True, - }, - ], - }, - ], -}] diff --git a/couchpotato/core/media/movie/providers/metadata/xbmc/main.py b/couchpotato/core/media/movie/providers/metadata/xbmc/main.py deleted file mode 100644 index 61f3647..0000000 --- a/couchpotato/core/media/movie/providers/metadata/xbmc/main.py +++ /dev/null @@ -1,174 +0,0 @@ -from couchpotato.core.media.movie.providers.metadata.base import MovieMetaData -from couchpotato.core.helpers.encoding import toUnicode -from couchpotato.core.helpers.variable import getTitle -from couchpotato.core.logger import CPLog -from xml.etree.ElementTree import Element, SubElement, tostring -import os -import re -import traceback -import xml.dom.minidom - -log = CPLog(__name__) - -class XBMC(MovieMetaData): - - def getFanartName(self, name, root): - return self.createMetaName(self.conf('meta_fanart_name'), name, root) - - def getThumbnailName(self, name, root): - return self.createMetaName(self.conf('meta_thumbnail_name'), name, root) - - def getNfoName(self, name, root): - return self.createMetaName(self.conf('meta_nfo_name'), name, root) - - def createMetaName(self, basename, name, root): - return os.path.join(root, basename.replace('%s', name)) - - def getNfo(self, movie_info = None, data = None): - if not data: data = {} - if not movie_info: movie_info = {} - - # return imdb url only - if self.conf('meta_url_only'): - return 'http://www.imdb.com/title/%s/' % toUnicode(data['identifier']) - - nfoxml = Element('movie') - - # Title - try: - el = SubElement(nfoxml, 'title') - el.text = toUnicode(getTitle(data)) - except: - pass - - # IMDB id - try: - el = SubElement(nfoxml, 'id') - el.text = toUnicode(data['identifier']) - except: - pass - - # Runtime - try: - runtime = SubElement(nfoxml, 'runtime') - runtime.text = '%s min' % movie_info.get('runtime') - except: - pass - - # Other values - types = ['year', 'mpaa', 'originaltitle:original_title', 'outline', 'plot', 'tagline', 'premiered:released'] - for type in types: - - if ':' in type: - name, type = type.split(':') - else: - name = type - - try: - if movie_info.get(type): - el = SubElement(nfoxml, name) - el.text = toUnicode(movie_info.get(type, '')) - except: - pass - - # Rating - for rating_type in ['imdb', 'rotten', 'tmdb']: - try: - r, v = movie_info['rating'][rating_type] - rating = SubElement(nfoxml, 'rating') - rating.text = str(r) - votes = SubElement(nfoxml, 'votes') - votes.text = str(v) - break - except: - log.debug('Failed adding rating info from %s: %s', (rating_type, traceback.format_exc())) - - # Genre - for genre in movie_info.get('genres', []): - genres = SubElement(nfoxml, 'genre') - genres.text = toUnicode(genre) - - # Actors - for actor_name in movie_info.get('actor_roles', {}): - role_name = movie_info['actor_roles'][actor_name] - - actor = SubElement(nfoxml, 'actor') - name = SubElement(actor, 'name') - name.text = toUnicode(actor_name) - if role_name: - role = SubElement(actor, 'role') - role.text = toUnicode(role_name) - if movie_info['images']['actors'].get(actor_name): - thumb = SubElement(actor, 'thumb') - thumb.text = toUnicode(movie_info['images']['actors'].get(actor_name)) - - # Directors - for director_name in movie_info.get('directors', []): - director = SubElement(nfoxml, 'director') - director.text = toUnicode(director_name) - - # Writers - for writer in movie_info.get('writers', []): - writers = SubElement(nfoxml, 'credits') - writers.text = toUnicode(writer) - - # Sets or collections - collection_name = movie_info.get('collection') - if collection_name: - collection = SubElement(nfoxml, 'set') - collection.text = toUnicode(collection_name) - sorttitle = SubElement(nfoxml, 'sorttitle') - sorttitle.text = '%s %s' % (toUnicode(collection_name), movie_info.get('year')) - - # Images - for image_url in movie_info['images']['poster_original']: - image = SubElement(nfoxml, 'thumb') - image.text = toUnicode(image_url) - fanart = SubElement(nfoxml, 'fanart') - for image_url in movie_info['images']['backdrop_original']: - image = SubElement(fanart, 'thumb') - image.text = toUnicode(image_url) - - # Add trailer if found - trailer_found = False - if data.get('renamed_files'): - for filename in data.get('renamed_files'): - if 'trailer' in filename: - trailer = SubElement(nfoxml, 'trailer') - trailer.text = toUnicode(filename) - trailer_found = True - if not trailer_found and data['files'].get('trailer'): - trailer = SubElement(nfoxml, 'trailer') - trailer.text = toUnicode(data['files']['trailer'][0]) - - # Add file metadata - fileinfo = SubElement(nfoxml, 'fileinfo') - streamdetails = SubElement(fileinfo, 'streamdetails') - - # Video data - if data['meta_data'].get('video'): - video = SubElement(streamdetails, 'video') - codec = SubElement(video, 'codec') - codec.text = toUnicode(data['meta_data']['video']) - aspect = SubElement(video, 'aspect') - aspect.text = str(data['meta_data']['aspect']) - width = SubElement(video, 'width') - width.text = str(data['meta_data']['resolution_width']) - height = SubElement(video, 'height') - height.text = str(data['meta_data']['resolution_height']) - - # Audio data - if data['meta_data'].get('audio'): - audio = SubElement(streamdetails, 'audio') - codec = SubElement(audio, 'codec') - codec.text = toUnicode(data['meta_data'].get('audio')) - channels = SubElement(audio, 'channels') - channels.text = toUnicode(data['meta_data'].get('audio_channels')) - - # Clean up the xml and return it - nfoxml = xml.dom.minidom.parseString(tostring(nfoxml)) - xml_string = nfoxml.toprettyxml(indent = ' ') - text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+\g<1>') + end = data.find('', start) + page_title = data[start + len(''):end].strip().split('-') + + name = page_title[0].strip() + year = page_title[1].strip()[-4:] + return self.search(name, year) + except: + log.error('Failed parsing page for title and year: %s', traceback.format_exc()) + diff --git a/couchpotato/core/media/movie/providers/userscript/allocine/__init__.py b/couchpotato/core/media/movie/providers/userscript/allocine/__init__.py deleted file mode 100644 index d272a7f..0000000 --- a/couchpotato/core/media/movie/providers/userscript/allocine/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import AlloCine - - -def start(): - return AlloCine() diff --git a/couchpotato/core/media/movie/providers/userscript/allocine/main.py b/couchpotato/core/media/movie/providers/userscript/allocine/main.py deleted file mode 100644 index c3400ee..0000000 --- a/couchpotato/core/media/movie/providers/userscript/allocine/main.py +++ /dev/null @@ -1,32 +0,0 @@ -from couchpotato.core.logger import CPLog -import traceback -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase - -log = CPLog(__name__) - - -class AlloCine(UserscriptBase): - - includes = ['http://www.allocine.fr/film/*'] - - def getMovie(self, url): - - if not 'fichefilm_gen_cfilm' in url: - return 'Url isn\'t from a movie' - - try: - data = self.getUrl(url) - except: - return - - try: - start = data.find('<title>') - end = data.find('', start) - page_title = data[start + len(''):end].strip().split('-') - - name = page_title[0].strip() - year = page_title[1].strip()[-4:] - return self.search(name, year) - except: - log.error('Failed parsing page for title and year: %s', traceback.format_exc()) - diff --git a/couchpotato/core/media/movie/providers/userscript/appletrailers.py b/couchpotato/core/media/movie/providers/userscript/appletrailers.py new file mode 100644 index 0000000..af201ed --- /dev/null +++ b/couchpotato/core/media/movie/providers/userscript/appletrailers.py @@ -0,0 +1,24 @@ +from couchpotato.core.media._base.providers.userscript.base import UserscriptBase +import re + +autoload = 'AppleTrailers' + + +class AppleTrailers(UserscriptBase): + + includes = ['http://trailers.apple.com/trailers/*'] + + def getMovie(self, url): + + try: + data = self.getUrl(url) + except: + return + + name = re.search("trailerTitle.*=.*\'(?P<name>.*)\';", data) + name = name.group('name').decode('string_escape') + + date = re.search("releaseDate.*=.*\'(?P<date>.*)\';", data) + year = date.group('date')[:4] + + return self.search(name, year) diff --git a/couchpotato/core/media/movie/providers/userscript/appletrailers/__init__.py b/couchpotato/core/media/movie/providers/userscript/appletrailers/__init__.py deleted file mode 100644 index 28eedbb..0000000 --- a/couchpotato/core/media/movie/providers/userscript/appletrailers/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import AppleTrailers - - -def start(): - return AppleTrailers() diff --git a/couchpotato/core/media/movie/providers/userscript/appletrailers/main.py b/couchpotato/core/media/movie/providers/userscript/appletrailers/main.py deleted file mode 100644 index 41b72c4..0000000 --- a/couchpotato/core/media/movie/providers/userscript/appletrailers/main.py +++ /dev/null @@ -1,22 +0,0 @@ -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase -import re - - -class AppleTrailers(UserscriptBase): - - includes = ['http://trailers.apple.com/trailers/*'] - - def getMovie(self, url): - - try: - data = self.getUrl(url) - except: - return - - name = re.search("trailerTitle.*=.*\'(?P<name>.*)\';", data) - name = name.group('name').decode('string_escape') - - date = re.search("releaseDate.*=.*\'(?P<date>.*)\';", data) - year = date.group('date')[:4] - - return self.search(name, year) diff --git a/couchpotato/core/media/movie/providers/userscript/criticker.py b/couchpotato/core/media/movie/providers/userscript/criticker.py new file mode 100644 index 0000000..cc0bee8 --- /dev/null +++ b/couchpotato/core/media/movie/providers/userscript/criticker.py @@ -0,0 +1,8 @@ +from couchpotato.core.media._base.providers.userscript.base import UserscriptBase + +autoload = 'Criticker' + + +class Criticker(UserscriptBase): + + includes = ['http://www.criticker.com/film/*'] diff --git a/couchpotato/core/media/movie/providers/userscript/criticker/__init__.py b/couchpotato/core/media/movie/providers/userscript/criticker/__init__.py deleted file mode 100644 index 550855e..0000000 --- a/couchpotato/core/media/movie/providers/userscript/criticker/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import Criticker - - -def start(): - return Criticker() diff --git a/couchpotato/core/media/movie/providers/userscript/criticker/main.py b/couchpotato/core/media/movie/providers/userscript/criticker/main.py deleted file mode 100644 index 7ab63c9..0000000 --- a/couchpotato/core/media/movie/providers/userscript/criticker/main.py +++ /dev/null @@ -1,6 +0,0 @@ -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase - - -class Criticker(UserscriptBase): - - includes = ['http://www.criticker.com/film/*'] diff --git a/couchpotato/core/media/movie/providers/userscript/filmweb.py b/couchpotato/core/media/movie/providers/userscript/filmweb.py new file mode 100644 index 0000000..f4e3652 --- /dev/null +++ b/couchpotato/core/media/movie/providers/userscript/filmweb.py @@ -0,0 +1,32 @@ +from couchpotato.core.media._base.providers.userscript.base import UserscriptBase +import re + +autoload = 'Filmweb' + + +class Filmweb(UserscriptBase): + + version = 2 + + includes = ['http://www.filmweb.pl/film/*'] + + def getMovie(self, url): + + cookie = {'Cookie': 'welcomeScreen=welcome_screen'} + + try: + data = self.urlopen(url, headers = cookie) + except: + return + + name = re.search("<h2.*?class=\"text-large caption\">(?P<name>[^<]+)</h2>", data) + + if name is None: + name = re.search("<a.*?property=\"v:name\".*?>(?P<name>[^<]+)</a>", data) + + name = name.group('name').decode('string_escape') + + year = re.search("<span.*?id=filmYear.*?>\((?P<year>[^\)]+)\).*?</span>", data) + year = year.group('year') + + return self.search(name, year) diff --git a/couchpotato/core/media/movie/providers/userscript/filmweb/__init__.py b/couchpotato/core/media/movie/providers/userscript/filmweb/__init__.py deleted file mode 100644 index 38354b4..0000000 --- a/couchpotato/core/media/movie/providers/userscript/filmweb/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import Filmweb - - -def start(): - return Filmweb() diff --git a/couchpotato/core/media/movie/providers/userscript/filmweb/main.py b/couchpotato/core/media/movie/providers/userscript/filmweb/main.py deleted file mode 100644 index 18e5d30..0000000 --- a/couchpotato/core/media/movie/providers/userscript/filmweb/main.py +++ /dev/null @@ -1,30 +0,0 @@ -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase -import re - - -class Filmweb(UserscriptBase): - - version = 2 - - includes = ['http://www.filmweb.pl/film/*'] - - def getMovie(self, url): - - cookie = {'Cookie': 'welcomeScreen=welcome_screen'} - - try: - data = self.urlopen(url, headers = cookie) - except: - return - - name = re.search("<h2.*?class=\"text-large caption\">(?P<name>[^<]+)</h2>", data) - - if name is None: - name = re.search("<a.*?property=\"v:name\".*?>(?P<name>[^<]+)</a>", data) - - name = name.group('name').decode('string_escape') - - year = re.search("<span.*?id=filmYear.*?>\((?P<year>[^\)]+)\).*?</span>", data) - year = year.group('year') - - return self.search(name, year) diff --git a/couchpotato/core/media/movie/providers/userscript/flickchart.py b/couchpotato/core/media/movie/providers/userscript/flickchart.py new file mode 100644 index 0000000..2df0acf --- /dev/null +++ b/couchpotato/core/media/movie/providers/userscript/flickchart.py @@ -0,0 +1,32 @@ +from couchpotato.core.event import fireEvent +from couchpotato.core.logger import CPLog +from couchpotato.core.media._base.providers.userscript.base import UserscriptBase +import traceback + +log = CPLog(__name__) + +autoload = 'Flickchart' + + +class Flickchart(UserscriptBase): + + includes = ['http://www.flickchart.com/movie/*'] + + def getMovie(self, url): + + try: + data = self.getUrl(url) + except: + return + + try: + start = data.find('<title>') + end = data.find('', start) + page_title = data[start + len(''):end].strip().split('-') + + year_name = fireEvent('scanner.name_year', page_title[0], single = True) + + return self.search(**year_name) + except: + log.error('Failed parsing page for title and year: %s', traceback.format_exc()) + diff --git a/couchpotato/core/media/movie/providers/userscript/flickchart/__init__.py b/couchpotato/core/media/movie/providers/userscript/flickchart/__init__.py deleted file mode 100644 index 5ff48e5..0000000 --- a/couchpotato/core/media/movie/providers/userscript/flickchart/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import Flickchart - - -def start(): - return Flickchart() diff --git a/couchpotato/core/media/movie/providers/userscript/flickchart/main.py b/couchpotato/core/media/movie/providers/userscript/flickchart/main.py deleted file mode 100644 index 6c2837b..0000000 --- a/couchpotato/core/media/movie/providers/userscript/flickchart/main.py +++ /dev/null @@ -1,30 +0,0 @@ -from couchpotato.core.event import fireEvent -from couchpotato.core.logger import CPLog -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase -import traceback - -log = CPLog(__name__) - - -class Flickchart(UserscriptBase): - - includes = ['http://www.flickchart.com/movie/*'] - - def getMovie(self, url): - - try: - data = self.getUrl(url) - except: - return - - try: - start = data.find('<title>') - end = data.find('', start) - page_title = data[start + len(''):end].strip().split('-') - - year_name = fireEvent('scanner.name_year', page_title[0], single = True) - - return self.search(**year_name) - except: - log.error('Failed parsing page for title and year: %s', traceback.format_exc()) - diff --git a/couchpotato/core/media/movie/providers/userscript/imdb.py b/couchpotato/core/media/movie/providers/userscript/imdb.py new file mode 100644 index 0000000..dccc483 --- /dev/null +++ b/couchpotato/core/media/movie/providers/userscript/imdb.py @@ -0,0 +1,12 @@ +from couchpotato.core.helpers.variable import getImdb +from couchpotato.core.media._base.providers.userscript.base import UserscriptBase + +autoload = 'IMDB' + + +class IMDB(UserscriptBase): + + includes = ['*://*.imdb.com/title/tt*', '*://imdb.com/title/tt*'] + + def getMovie(self, url): + return self.getInfo(getImdb(url)) diff --git a/couchpotato/core/media/movie/providers/userscript/imdb/__init__.py b/couchpotato/core/media/movie/providers/userscript/imdb/__init__.py deleted file mode 100644 index e310cc3..0000000 --- a/couchpotato/core/media/movie/providers/userscript/imdb/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import IMDB - - -def start(): - return IMDB() diff --git a/couchpotato/core/media/movie/providers/userscript/imdb/main.py b/couchpotato/core/media/movie/providers/userscript/imdb/main.py deleted file mode 100644 index 1436393..0000000 --- a/couchpotato/core/media/movie/providers/userscript/imdb/main.py +++ /dev/null @@ -1,11 +0,0 @@ -from couchpotato.core.event import fireEvent -from couchpotato.core.helpers.variable import getImdb -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase - - -class IMDB(UserscriptBase): - - includes = ['*://*.imdb.com/title/tt*', '*://imdb.com/title/tt*'] - - def getMovie(self, url): - return self.getInfo(getImdb(url)) diff --git a/couchpotato/core/media/movie/providers/userscript/letterboxd.py b/couchpotato/core/media/movie/providers/userscript/letterboxd.py new file mode 100644 index 0000000..43b5d30 --- /dev/null +++ b/couchpotato/core/media/movie/providers/userscript/letterboxd.py @@ -0,0 +1,8 @@ +from couchpotato.core.media._base.providers.userscript.base import UserscriptBase + +autoload = 'Letterboxd' + + +class Letterboxd(UserscriptBase): + + includes = ['*://letterboxd.com/film/*'] diff --git a/couchpotato/core/media/movie/providers/userscript/letterboxd/__init__.py b/couchpotato/core/media/movie/providers/userscript/letterboxd/__init__.py deleted file mode 100644 index 78ef77b..0000000 --- a/couchpotato/core/media/movie/providers/userscript/letterboxd/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import Letterboxd - - -def start(): - return Letterboxd() diff --git a/couchpotato/core/media/movie/providers/userscript/letterboxd/main.py b/couchpotato/core/media/movie/providers/userscript/letterboxd/main.py deleted file mode 100644 index 852c1b7..0000000 --- a/couchpotato/core/media/movie/providers/userscript/letterboxd/main.py +++ /dev/null @@ -1,6 +0,0 @@ -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase - - -class Letterboxd(UserscriptBase): - - includes = ['*://letterboxd.com/film/*'] diff --git a/couchpotato/core/media/movie/providers/userscript/moviemeter.py b/couchpotato/core/media/movie/providers/userscript/moviemeter.py new file mode 100644 index 0000000..4c9bb22 --- /dev/null +++ b/couchpotato/core/media/movie/providers/userscript/moviemeter.py @@ -0,0 +1,8 @@ +from couchpotato.core.media._base.providers.userscript.base import UserscriptBase + +autoload = 'MovieMeter' + + +class MovieMeter(UserscriptBase): + + includes = ['http://*.moviemeter.nl/film/*', 'http://moviemeter.nl/film/*'] diff --git a/couchpotato/core/media/movie/providers/userscript/moviemeter/__init__.py b/couchpotato/core/media/movie/providers/userscript/moviemeter/__init__.py deleted file mode 100644 index aae8ca7..0000000 --- a/couchpotato/core/media/movie/providers/userscript/moviemeter/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import MovieMeter - - -def start(): - return MovieMeter() diff --git a/couchpotato/core/media/movie/providers/userscript/moviemeter/main.py b/couchpotato/core/media/movie/providers/userscript/moviemeter/main.py deleted file mode 100644 index f1b254f..0000000 --- a/couchpotato/core/media/movie/providers/userscript/moviemeter/main.py +++ /dev/null @@ -1,6 +0,0 @@ -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase - - -class MovieMeter(UserscriptBase): - - includes = ['http://*.moviemeter.nl/film/*', 'http://moviemeter.nl/film/*'] diff --git a/couchpotato/core/media/movie/providers/userscript/moviesio.py b/couchpotato/core/media/movie/providers/userscript/moviesio.py new file mode 100644 index 0000000..0381d64 --- /dev/null +++ b/couchpotato/core/media/movie/providers/userscript/moviesio.py @@ -0,0 +1,8 @@ +from couchpotato.core.media._base.providers.userscript.base import UserscriptBase + +autoload = 'MoviesIO' + + +class MoviesIO(UserscriptBase): + + includes = ['*://movies.io/m/*'] diff --git a/couchpotato/core/media/movie/providers/userscript/moviesio/__init__.py b/couchpotato/core/media/movie/providers/userscript/moviesio/__init__.py deleted file mode 100644 index 3f0497f..0000000 --- a/couchpotato/core/media/movie/providers/userscript/moviesio/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import MoviesIO - - -def start(): - return MoviesIO() diff --git a/couchpotato/core/media/movie/providers/userscript/moviesio/main.py b/couchpotato/core/media/movie/providers/userscript/moviesio/main.py deleted file mode 100644 index a466baf..0000000 --- a/couchpotato/core/media/movie/providers/userscript/moviesio/main.py +++ /dev/null @@ -1,6 +0,0 @@ -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase - - -class MoviesIO(UserscriptBase): - - includes = ['*://movies.io/m/*'] diff --git a/couchpotato/core/media/movie/providers/userscript/reddit.py b/couchpotato/core/media/movie/providers/userscript/reddit.py new file mode 100644 index 0000000..3ab1d08 --- /dev/null +++ b/couchpotato/core/media/movie/providers/userscript/reddit.py @@ -0,0 +1,19 @@ +from couchpotato import fireEvent +from couchpotato.core.helpers.variable import splitString +from couchpotato.core.media._base.providers.userscript.base import UserscriptBase + +autoload = 'Reddit' + + +class Reddit(UserscriptBase): + + includes = ['*://www.reddit.com/r/Ijustwatched/comments/*'] + + def getMovie(self, url): + name = splitString(url, '/')[-1] + if name.startswith('ijw_'): + name = name[4:] + + year_name = fireEvent('scanner.name_year', name, single = True) + + return self.search(year_name.get('name'), year_name.get('year')) diff --git a/couchpotato/core/media/movie/providers/userscript/reddit/__init__.py b/couchpotato/core/media/movie/providers/userscript/reddit/__init__.py deleted file mode 100644 index d803bb5..0000000 --- a/couchpotato/core/media/movie/providers/userscript/reddit/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import Reddit - - -def start(): - return Reddit() diff --git a/couchpotato/core/media/movie/providers/userscript/reddit/main.py b/couchpotato/core/media/movie/providers/userscript/reddit/main.py deleted file mode 100644 index ec59e69..0000000 --- a/couchpotato/core/media/movie/providers/userscript/reddit/main.py +++ /dev/null @@ -1,17 +0,0 @@ -from couchpotato import fireEvent -from couchpotato.core.helpers.variable import splitString -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase - - -class Reddit(UserscriptBase): - - includes = ['*://www.reddit.com/r/Ijustwatched/comments/*'] - - def getMovie(self, url): - name = splitString(url, '/')[-1] - if name.startswith('ijw_'): - name = name[4:] - - year_name = fireEvent('scanner.name_year', name, single = True) - - return self.search(year_name.get('name'), year_name.get('year')) diff --git a/couchpotato/core/media/movie/providers/userscript/rottentomatoes.py b/couchpotato/core/media/movie/providers/userscript/rottentomatoes.py new file mode 100644 index 0000000..cb66146 --- /dev/null +++ b/couchpotato/core/media/movie/providers/userscript/rottentomatoes.py @@ -0,0 +1,41 @@ +from couchpotato.core.logger import CPLog +from couchpotato.core.media._base.providers.userscript.base import UserscriptBase +import re +import traceback + +log = CPLog(__name__) + +autoload = 'RottenTomatoes' + + +class RottenTomatoes(UserscriptBase): + + includes = ['*://www.rottentomatoes.com/m/*/'] + excludes = ['*://www.rottentomatoes.com/m/*/*/'] + + version = 2 + + def getMovie(self, url): + + try: + data = self.getUrl(url) + except: + return + + try: + name = None + year = None + metas = re.findall("property=\"(video:release_date|og:title)\" content=\"([^\"]*)\"", data) + + for meta in metas: + mname, mvalue = meta + if mname == 'og:title': + name = mvalue.decode('unicode_escape') + elif mname == 'video:release_date': + year = mvalue[:4] + + if name and year: + return self.search(name, year) + + except: + log.error('Failed parsing page for title and year: %s', traceback.format_exc()) diff --git a/couchpotato/core/media/movie/providers/userscript/rottentomatoes/__init__.py b/couchpotato/core/media/movie/providers/userscript/rottentomatoes/__init__.py deleted file mode 100644 index b041d5a..0000000 --- a/couchpotato/core/media/movie/providers/userscript/rottentomatoes/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import RottenTomatoes - - -def start(): - return RottenTomatoes() diff --git a/couchpotato/core/media/movie/providers/userscript/rottentomatoes/main.py b/couchpotato/core/media/movie/providers/userscript/rottentomatoes/main.py deleted file mode 100644 index 0b3145e..0000000 --- a/couchpotato/core/media/movie/providers/userscript/rottentomatoes/main.py +++ /dev/null @@ -1,39 +0,0 @@ -from couchpotato.core.logger import CPLog -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase -import re -import traceback - -log = CPLog(__name__) - - -class RottenTomatoes(UserscriptBase): - - includes = ['*://www.rottentomatoes.com/m/*/'] - excludes = ['*://www.rottentomatoes.com/m/*/*/'] - - version = 2 - - def getMovie(self, url): - - try: - data = self.getUrl(url) - except: - return - - try: - name = None - year = None - metas = re.findall("property=\"(video:release_date|og:title)\" content=\"([^\"]*)\"", data) - - for meta in metas: - mname, mvalue = meta - if mname == 'og:title': - name = mvalue.decode('unicode_escape') - elif mname == 'video:release_date': - year = mvalue[:4] - - if name and year: - return self.search(name, year) - - except: - log.error('Failed parsing page for title and year: %s', traceback.format_exc()) diff --git a/couchpotato/core/media/movie/providers/userscript/sharethe.py b/couchpotato/core/media/movie/providers/userscript/sharethe.py new file mode 100644 index 0000000..ef2f537 --- /dev/null +++ b/couchpotato/core/media/movie/providers/userscript/sharethe.py @@ -0,0 +1,8 @@ +from couchpotato.core.media._base.providers.userscript.base import UserscriptBase + +autoload = 'ShareThe' + + +class ShareThe(UserscriptBase): + + includes = ['http://*.sharethe.tv/movies/*', 'http://sharethe.tv/movies/*'] diff --git a/couchpotato/core/media/movie/providers/userscript/sharethe/__init__.py b/couchpotato/core/media/movie/providers/userscript/sharethe/__init__.py deleted file mode 100644 index cf553da..0000000 --- a/couchpotato/core/media/movie/providers/userscript/sharethe/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import ShareThe - - -def start(): - return ShareThe() diff --git a/couchpotato/core/media/movie/providers/userscript/sharethe/main.py b/couchpotato/core/media/movie/providers/userscript/sharethe/main.py deleted file mode 100644 index ce11229..0000000 --- a/couchpotato/core/media/movie/providers/userscript/sharethe/main.py +++ /dev/null @@ -1,6 +0,0 @@ -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase - - -class ShareThe(UserscriptBase): - - includes = ['http://*.sharethe.tv/movies/*', 'http://sharethe.tv/movies/*'] diff --git a/couchpotato/core/media/movie/providers/userscript/tmdb.py b/couchpotato/core/media/movie/providers/userscript/tmdb.py new file mode 100644 index 0000000..71ab0d7 --- /dev/null +++ b/couchpotato/core/media/movie/providers/userscript/tmdb.py @@ -0,0 +1,18 @@ +from couchpotato.core.event import fireEvent +from couchpotato.core.media._base.providers.userscript.base import UserscriptBase +import re + +autoload = 'TMDB' + + +class TMDB(UserscriptBase): + + includes = ['http://www.themoviedb.org/movie/*'] + + def getMovie(self, url): + match = re.search('(?P<id>\d+)', url) + movie = fireEvent('movie.info_by_tmdb', identifier = match.group('id'), extended = False, merge = True) + + if movie['imdb']: + return self.getInfo(movie['imdb']) + diff --git a/couchpotato/core/media/movie/providers/userscript/tmdb/__init__.py b/couchpotato/core/media/movie/providers/userscript/tmdb/__init__.py deleted file mode 100644 index 609f364..0000000 --- a/couchpotato/core/media/movie/providers/userscript/tmdb/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import TMDB - - -def start(): - return TMDB() diff --git a/couchpotato/core/media/movie/providers/userscript/tmdb/main.py b/couchpotato/core/media/movie/providers/userscript/tmdb/main.py deleted file mode 100644 index 9638bea..0000000 --- a/couchpotato/core/media/movie/providers/userscript/tmdb/main.py +++ /dev/null @@ -1,16 +0,0 @@ -from couchpotato.core.event import fireEvent -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase -import re - - -class TMDB(UserscriptBase): - - includes = ['http://www.themoviedb.org/movie/*'] - - def getMovie(self, url): - match = re.search('(?P<id>\d+)', url) - movie = fireEvent('movie.info_by_tmdb', identifier = match.group('id'), extended = False, merge = True) - - if movie['imdb']: - return self.getInfo(movie['imdb']) - diff --git a/couchpotato/core/media/movie/providers/userscript/trakt.py b/couchpotato/core/media/movie/providers/userscript/trakt.py new file mode 100644 index 0000000..cd40c69 --- /dev/null +++ b/couchpotato/core/media/movie/providers/userscript/trakt.py @@ -0,0 +1,9 @@ +from couchpotato.core.media._base.providers.userscript.base import UserscriptBase + +autoload = 'Trakt' + + +class Trakt(UserscriptBase): + + includes = ['http://trakt.tv/movie/*', 'http://*.trakt.tv/movie/*'] + excludes = ['http://trakt.tv/movie/*/*', 'http://*.trakt.tv/movie/*/*'] diff --git a/couchpotato/core/media/movie/providers/userscript/trakt/__init__.py b/couchpotato/core/media/movie/providers/userscript/trakt/__init__.py deleted file mode 100644 index ddba440..0000000 --- a/couchpotato/core/media/movie/providers/userscript/trakt/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import Trakt - - -def start(): - return Trakt() diff --git a/couchpotato/core/media/movie/providers/userscript/trakt/main.py b/couchpotato/core/media/movie/providers/userscript/trakt/main.py deleted file mode 100644 index 17cfddd..0000000 --- a/couchpotato/core/media/movie/providers/userscript/trakt/main.py +++ /dev/null @@ -1,7 +0,0 @@ -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase - - -class Trakt(UserscriptBase): - - includes = ['http://trakt.tv/movie/*', 'http://*.trakt.tv/movie/*'] - excludes = ['http://trakt.tv/movie/*/*', 'http://*.trakt.tv/movie/*/*'] diff --git a/couchpotato/core/media/movie/providers/userscript/whiwa.py b/couchpotato/core/media/movie/providers/userscript/whiwa.py new file mode 100644 index 0000000..bd602d2 --- /dev/null +++ b/couchpotato/core/media/movie/providers/userscript/whiwa.py @@ -0,0 +1,8 @@ +from couchpotato.core.media._base.providers.userscript.base import UserscriptBase + +autoload = 'WHiWA' + + +class WHiWA(UserscriptBase): + + includes = ['http://whiwa.net/stats/movie/*'] diff --git a/couchpotato/core/media/movie/providers/userscript/whiwa/__init__.py b/couchpotato/core/media/movie/providers/userscript/whiwa/__init__.py deleted file mode 100644 index 3b14f47..0000000 --- a/couchpotato/core/media/movie/providers/userscript/whiwa/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import WHiWA - - -def start(): - return WHiWA() diff --git a/couchpotato/core/media/movie/providers/userscript/whiwa/main.py b/couchpotato/core/media/movie/providers/userscript/whiwa/main.py deleted file mode 100644 index e0a0120..0000000 --- a/couchpotato/core/media/movie/providers/userscript/whiwa/main.py +++ /dev/null @@ -1,6 +0,0 @@ -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase - - -class WHiWA(UserscriptBase): - - includes = ['http://whiwa.net/stats/movie/*'] diff --git a/couchpotato/core/media/movie/providers/userscript/youteather.py b/couchpotato/core/media/movie/providers/userscript/youteather.py new file mode 100644 index 0000000..3a35df3 --- /dev/null +++ b/couchpotato/core/media/movie/providers/userscript/youteather.py @@ -0,0 +1,15 @@ +from couchpotato.core.media._base.providers.userscript.base import UserscriptBase +import re + +autoload = 'YouTheater' + + +class YouTheater(UserscriptBase): + id_re = re.compile("view\.php\?id=(\d+)") + includes = ['http://www.youtheater.com/view.php?id=*', 'http://youtheater.com/view.php?id=*', + 'http://www.sratim.co.il/view.php?id=*', 'http://sratim.co.il/view.php?id=*'] + + def getMovie(self, url): + id = self.id_re.findall(url)[0] + url = 'http://www.youtheater.com/view.php?id=%s' % id + return super(YouTheater, self).getMovie(url) diff --git a/couchpotato/core/media/movie/providers/userscript/youteather/__init__.py b/couchpotato/core/media/movie/providers/userscript/youteather/__init__.py deleted file mode 100644 index 8eb44c7..0000000 --- a/couchpotato/core/media/movie/providers/userscript/youteather/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .main import YouTheater - - -def start(): - return YouTheater() diff --git a/couchpotato/core/media/movie/providers/userscript/youteather/main.py b/couchpotato/core/media/movie/providers/userscript/youteather/main.py deleted file mode 100644 index 34c9768..0000000 --- a/couchpotato/core/media/movie/providers/userscript/youteather/main.py +++ /dev/null @@ -1,12 +0,0 @@ -from couchpotato.core.media._base.providers.userscript.base import UserscriptBase -import re - -class YouTheater(UserscriptBase): - id_re = re.compile("view\.php\?id=(\d+)") - includes = ['http://www.youtheater.com/view.php?id=*', 'http://youtheater.com/view.php?id=*', - 'http://www.sratim.co.il/view.php?id=*', 'http://sratim.co.il/view.php?id=*'] - - def getMovie(self, url): - id = self.id_re.findall(url)[0] - url = 'http://www.youtheater.com/view.php?id=%s' % id - return super(YouTheater, self).getMovie(url) diff --git a/couchpotato/core/media/movie/searcher.py b/couchpotato/core/media/movie/searcher.py new file mode 100644 index 0000000..192040f --- /dev/null +++ b/couchpotato/core/media/movie/searcher.py @@ -0,0 +1,418 @@ +from couchpotato import get_db +from couchpotato.api import addApiView +from couchpotato.core.event import addEvent, fireEvent, fireEventAsync +from couchpotato.core.helpers.encoding import simplifyString +from couchpotato.core.helpers.variable import getTitle, possibleTitles, getImdb +from couchpotato.core.logger import CPLog +from couchpotato.core.media._base.searcher.base import SearcherBase +from couchpotato.core.media.movie import MovieTypeBase +from couchpotato.environment import Env +from datetime import date +import random +import re +import time +import traceback + +log = CPLog(__name__) + + +class MovieSearcher(SearcherBase, MovieTypeBase): + + in_progress = False + + def __init__(self): + super(MovieSearcher, self).__init__() + + addEvent('movie.searcher.all', self.searchAll) + addEvent('movie.searcher.all_view', self.searchAllView) + addEvent('movie.searcher.single', self.single) + addEvent('movie.searcher.try_next_release', self.tryNextRelease) + addEvent('movie.searcher.could_be_released', self.couldBeReleased) + addEvent('searcher.correct_release', self.correctRelease) + addEvent('searcher.get_search_title', self.getSearchTitle) + + addApiView('movie.searcher.try_next', self.tryNextReleaseView, docs = { + 'desc': 'Marks the snatched results as ignored and try the next best release', + 'params': { + 'id': {'desc': 'The id of the movie'}, + }, + }) + + addApiView('movie.searcher.full_search', self.searchAllView, docs = { + 'desc': 'Starts a full search for all wanted movies', + }) + + addApiView('movie.searcher.progress', self.getProgress, docs = { + 'desc': 'Get the progress of current full search', + 'return': {'type': 'object', 'example': """{ + 'progress': False || object, total & to_go, +}"""}, + }) + + if self.conf('run_on_launch'): + addEvent('app.load', self.searchAll) + + def searchAllView(self, **kwargs): + + fireEventAsync('movie.searcher.all') + + return { + 'success': not self.in_progress + } + + def searchAll(self): + + if self.in_progress: + log.info('Search already in progress') + fireEvent('notify.frontend', type = 'movie.searcher.already_started', data = True, message = 'Full search already in progress') + return + + self.in_progress = True + fireEvent('notify.frontend', type = 'movie.searcher.started', data = True, message = 'Full search started') + + db = get_db() + + medias = [x['_id'] for x in db.run('media', 'with_status', 'active', with_doc = False)] + random.shuffle(medias) + + total = len(medias) + self.in_progress = { + 'total': total, + 'to_go': total, + } + + try: + search_protocols = fireEvent('searcher.protocols', single = True) + + for media_id in medias: + + media = db.run('media', 'to_dict', media_id) + + try: + self.single(media, search_protocols) + except IndexError: + log.error('Forcing library update for %s, if you see this often, please report: %s', (media['identifier'], traceback.format_exc())) + fireEvent('movie.update_info', media_id) + except: + log.error('Search failed for %s: %s', (media['identifier'], traceback.format_exc())) + + self.in_progress['to_go'] -= 1 + + # Break if CP wants to shut down + if self.shuttingDown(): + break + + except SearchSetupError: + pass + + self.in_progress = False + + def single(self, movie, search_protocols = None, manual = False): + + # Find out search type + try: + if not search_protocols: + search_protocols = fireEvent('searcher.protocols', single = True) + except SearchSetupError: + return + + if not movie['profile_id'] or (movie['status'] == 'done' and not manual): + log.debug('Movie doesn\'t have a profile or already done, assuming in manage tab.') + return + + pre_releases = fireEvent('quality.pre_releases', single = True) + release_dates = fireEvent('movie.update_release_dates', movie['_id'], merge = True) + + found_releases = [] + too_early_to_search = [] + + default_title = getTitle(movie) + if not default_title: + log.error('No proper info found for movie, removing it from library to cause it from having more issues.') + fireEvent('media.delete', movie['_id'], single = True) + return + + fireEvent('notify.frontend', type = 'movie.searcher.started', data = {'_id': movie['_id']}, message = 'Searching for "%s"' % default_title) + + db = get_db() + + profile = db.get('id', movie['profile_id']) + quality_order = fireEvent('quality.order', single = True) + media_releases = db.run('release', 'for_media', movie['_id']) + + ret = False + + for q_identifier in profile.get('qualities'): + index = profile['qualities'].index(q_identifier) + quality_custom = { + 'quality': q_identifier, + 'finish': profile['finish'][index], + 'wait_for': profile['wait_for'][index] + } + + if not self.conf('always_search') and not self.couldBeReleased(q_identifier in pre_releases, release_dates, movie['info']['year']): + too_early_to_search.append(q_identifier) + continue + + has_better_quality = 0 + + # See if better quality is available + for release in media_releases: + if quality_order.index(release['quality']) <= quality_order.index(q_identifier) and release['status'] not in ['available', 'ignored', 'failed']: + has_better_quality += 1 + + # Don't search for quality lower then already available. + if has_better_quality is 0: + + quality = fireEvent('quality.single', identifier = q_identifier, single = True) + log.info('Search for %s in %s', (default_title, quality['label'])) + + results = fireEvent('searcher.search', search_protocols, movie, quality, single = True) or [] + if len(results) == 0: + log.debug('Nothing found for %s in %s', (default_title, quality['label'])) + + # Check if movie isn't deleted while searching + if not fireEvent('media.get', movie.get('_id'), single = True): + break + + # Add them to this movie releases list + found_releases += fireEvent('release.create_from_search', results, movie, quality, single = True) + + # Try find a valid result and download it + if fireEvent('release.try_download_result', results, movie, quality_custom, manual, single = True): + ret = True + + # Remove releases that aren't found anymore + for release in db.run('release', 'for_media', movie['_id']): + if release.get('status') == 'available' and release.get('identifier') not in found_releases: + fireEvent('release.delete', release.get('_id'), single = True) + + else: + log.info('Better quality (%s) already available or snatched for %s', (quality['label'], default_title)) + fireEvent('media.restatus', movie['_id']) + break + + # Break if CP wants to shut down + if self.shuttingDown() or ret: + break + + if len(too_early_to_search) > 0: + log.info2('Too early to search for %s, %s', (too_early_to_search, default_title)) + + fireEvent('notify.frontend', type = 'movie.searcher.ended', data = {'_id': movie['_id']}) + + return ret + + def correctRelease(self, nzb = None, media = None, quality = None, **kwargs): + + if media.get('type') != 'movie': return + + media_title = fireEvent('searcher.get_search_title', media, single = True) + + imdb_results = kwargs.get('imdb_results', False) + retention = Env.setting('retention', section = 'nzb') + + if nzb.get('seeders') is None and 0 < retention < nzb.get('age', 0): + log.info2('Wrong: Outside retention, age is %s, needs %s or lower: %s', (nzb['age'], retention, nzb['name'])) + return False + + # Check for required and ignored words + if not fireEvent('searcher.correct_words', nzb['name'], media, single = True): + return False + + preferred_quality = fireEvent('quality.single', identifier = quality['identifier'], single = True) + + # Contains lower quality string + if fireEvent('searcher.contains_other_quality', nzb, movie_year = media['info']['year'], preferred_quality = preferred_quality, single = True): + log.info2('Wrong: %s, looking for %s', (nzb['name'], quality['label'])) + return False + + + # File to small + if nzb['size'] and preferred_quality['size_min'] > nzb['size']: + log.info2('Wrong: "%s" is too small to be %s. %sMB instead of the minimal of %sMB.', (nzb['name'], preferred_quality['label'], nzb['size'], preferred_quality['size_min'])) + return False + + # File to large + if nzb['size'] and preferred_quality.get('size_max') < nzb['size']: + log.info2('Wrong: "%s" is too large to be %s. %sMB instead of the maximum of %sMB.', (nzb['name'], preferred_quality['label'], nzb['size'], preferred_quality['size_max'])) + return False + + + # Provider specific functions + get_more = nzb.get('get_more_info') + if get_more: + get_more(nzb) + + extra_check = nzb.get('extra_check') + if extra_check and not extra_check(nzb): + return False + + + if imdb_results: + return True + + # Check if nzb contains imdb link + if getImdb(nzb.get('description', '')) == media['identifier']: + return True + + for raw_title in media['info']['titles']: + for movie_title in possibleTitles(raw_title): + movie_words = re.split('\W+', simplifyString(movie_title)) + + if fireEvent('searcher.correct_name', nzb['name'], movie_title, single = True): + # if no IMDB link, at least check year range 1 + if len(movie_words) > 2 and fireEvent('searcher.correct_year', nzb['name'], media['info']['year'], 1, single = True): + return True + + # if no IMDB link, at least check year + if len(movie_words) <= 2 and fireEvent('searcher.correct_year', nzb['name'], media['info']['year'], 0, single = True): + return True + + log.info("Wrong: %s, undetermined naming. Looking for '%s (%s)'", (nzb['name'], media_title, media['info']['year'])) + return False + + def couldBeReleased(self, is_pre_release, dates, year = None): + + now = int(time.time()) + now_year = date.today().year + now_month = date.today().month + + if (year is None or year < now_year - 1) and (not dates or (dates.get('theater', 0) == 0 and dates.get('dvd', 0) == 0)): + return True + else: + + # Don't allow movies with years to far in the future + add_year = 1 if now_month > 10 else 0 # Only allow +1 year if end of the year + if year is not None and year > (now_year + add_year): + return False + + # For movies before 1972 + if not dates or dates.get('theater', 0) < 0 or dates.get('dvd', 0) < 0: + return True + + if is_pre_release: + # Prerelease 1 week before theaters + if dates.get('theater') - 604800 < now: + return True + else: + # 12 weeks after theater release + if dates.get('theater') > 0 and dates.get('theater') + 7257600 < now: + return True + + if dates.get('dvd') > 0: + + # 4 weeks before dvd release + if dates.get('dvd') - 2419200 < now: + return True + + # Dvd should be released + if dates.get('dvd') < now: + return True + + + return False + + def tryNextReleaseView(self, id = None, **kwargs): + + trynext = self.tryNextRelease(id, manual = True) + + return { + 'success': trynext + } + + def tryNextRelease(self, media_id, manual = False): + + try: + db = get_db() + rels = db.run('media', 'with_status', media_id, status = ['snatched', 'done']) + + for rel in rels: + rel['status'] = 'ignored' + db.update(rel) + + movie_dict = db.run('media', 'to_dict', media_id) + log.info('Trying next release for: %s', getTitle(movie_dict)) + self.single(movie_dict, manual = manual) + + return True + + except: + log.error('Failed searching for next release: %s', traceback.format_exc()) + return False + + def getSearchTitle(self, media): + if media['type'] == 'movie': + return getTitle(media) + +class SearchSetupError(Exception): + pass + + +config = [{ + 'name': 'moviesearcher', + 'order': 20, + 'groups': [ + { + 'tab': 'searcher', + 'name': 'movie_searcher', + 'label': 'Movie search', + 'description': 'Search options for movies', + 'advanced': True, + 'options': [ + { + 'name': 'always_search', + 'default': False, + 'migrate_from': 'searcher', + 'type': 'bool', + 'label': 'Always search', + 'description': 'Search for movies even before there is a ETA. Enabling this will probably get you a lot of fakes.', + }, + { + 'name': 'run_on_launch', + 'migrate_from': 'searcher', + 'label': 'Run on launch', + 'advanced': True, + 'default': 0, + 'type': 'bool', + 'description': 'Force run the searcher after (re)start.', + }, + { + 'name': 'search_on_add', + 'label': 'Search after add', + 'advanced': True, + 'default': 1, + 'type': 'bool', + 'description': 'Disable this to only search for movies on cron.', + }, + { + 'name': 'cron_day', + 'migrate_from': 'searcher', + 'label': 'Day', + 'advanced': True, + 'default': '*', + 'type': 'string', + 'description': '<strong>*</strong>: Every day, <strong>*/2</strong>: Every 2 days, <strong>1</strong>: Every first of the month. See <a href="http://packages.python.org/APScheduler/cronschedule.html">APScheduler</a> for details.', + }, + { + 'name': 'cron_hour', + 'migrate_from': 'searcher', + 'label': 'Hour', + 'advanced': True, + 'default': random.randint(0, 23), + 'type': 'string', + 'description': '<strong>*</strong>: Every hour, <strong>*/8</strong>: Every 8 hours, <strong>3</strong>: At 3, midnight.', + }, + { + 'name': 'cron_minute', + 'migrate_from': 'searcher', + 'label': 'Minute', + 'advanced': True, + 'default': random.randint(0, 59), + 'type': 'string', + 'description': "Just keep it random, so the providers don't get DDOSed by every CP user on a 'full' hour." + }, + ], + }, + ], +}] diff --git a/couchpotato/core/media/movie/searcher/__init__.py b/couchpotato/core/media/movie/searcher/__init__.py deleted file mode 100644 index 4ae1ed3..0000000 --- a/couchpotato/core/media/movie/searcher/__init__.py +++ /dev/null @@ -1,74 +0,0 @@ -from .main import MovieSearcher -import random - - -def start(): - return MovieSearcher() - -config = [{ - 'name': 'moviesearcher', - 'order': 20, - 'groups': [ - { - 'tab': 'searcher', - 'name': 'movie_searcher', - 'label': 'Movie search', - 'description': 'Search options for movies', - 'advanced': True, - 'options': [ - { - 'name': 'always_search', - 'default': False, - 'migrate_from': 'searcher', - 'type': 'bool', - 'label': 'Always search', - 'description': 'Search for movies even before there is a ETA. Enabling this will probably get you a lot of fakes.', - }, - { - 'name': 'run_on_launch', - 'migrate_from': 'searcher', - 'label': 'Run on launch', - 'advanced': True, - 'default': 0, - 'type': 'bool', - 'description': 'Force run the searcher after (re)start.', - }, - { - 'name': 'search_on_add', - 'label': 'Search after add', - 'advanced': True, - 'default': 1, - 'type': 'bool', - 'description': 'Disable this to only search for movies on cron.', - }, - { - 'name': 'cron_day', - 'migrate_from': 'searcher', - 'label': 'Day', - 'advanced': True, - 'default': '*', - 'type': 'string', - 'description': '<strong>*</strong>: Every day, <strong>*/2</strong>: Every 2 days, <strong>1</strong>: Every first of the month. See <a href="http://packages.python.org/APScheduler/cronschedule.html">APScheduler</a> for details.', - }, - { - 'name': 'cron_hour', - 'migrate_from': 'searcher', - 'label': 'Hour', - 'advanced': True, - 'default': random.randint(0, 23), - 'type': 'string', - 'description': '<strong>*</strong>: Every hour, <strong>*/8</strong>: Every 8 hours, <strong>3</strong>: At 3, midnight.', - }, - { - 'name': 'cron_minute', - 'migrate_from': 'searcher', - 'label': 'Minute', - 'advanced': True, - 'default': random.randint(0, 59), - 'type': 'string', - 'description': "Just keep it random, so the providers don't get DDOSed by every CP user on a 'full' hour." - }, - ], - }, - ], -}] diff --git a/couchpotato/core/media/movie/searcher/main.py b/couchpotato/core/media/movie/searcher/main.py deleted file mode 100644 index dfc2ee2..0000000 --- a/couchpotato/core/media/movie/searcher/main.py +++ /dev/null @@ -1,349 +0,0 @@ -from couchpotato import get_db -from couchpotato.api import addApiView -from couchpotato.core.event import addEvent, fireEvent, fireEventAsync -from couchpotato.core.helpers.encoding import simplifyString -from couchpotato.core.helpers.variable import getTitle, possibleTitles, getImdb -from couchpotato.core.logger import CPLog -from couchpotato.core.media._base.searcher.base import SearcherBase -from couchpotato.core.media.movie import MovieTypeBase -from couchpotato.environment import Env -from datetime import date -import random -import re -import time -import traceback - -log = CPLog(__name__) - - -class MovieSearcher(SearcherBase, MovieTypeBase): - - in_progress = False - - def __init__(self): - super(MovieSearcher, self).__init__() - - addEvent('movie.searcher.all', self.searchAll) - addEvent('movie.searcher.all_view', self.searchAllView) - addEvent('movie.searcher.single', self.single) - addEvent('movie.searcher.try_next_release', self.tryNextRelease) - addEvent('movie.searcher.could_be_released', self.couldBeReleased) - addEvent('searcher.correct_release', self.correctRelease) - addEvent('searcher.get_search_title', self.getSearchTitle) - - addApiView('movie.searcher.try_next', self.tryNextReleaseView, docs = { - 'desc': 'Marks the snatched results as ignored and try the next best release', - 'params': { - 'id': {'desc': 'The id of the movie'}, - }, - }) - - addApiView('movie.searcher.full_search', self.searchAllView, docs = { - 'desc': 'Starts a full search for all wanted movies', - }) - - addApiView('movie.searcher.progress', self.getProgress, docs = { - 'desc': 'Get the progress of current full search', - 'return': {'type': 'object', 'example': """{ - 'progress': False || object, total & to_go, -}"""}, - }) - - if self.conf('run_on_launch'): - addEvent('app.load', self.searchAll) - - def searchAllView(self, **kwargs): - - fireEventAsync('movie.searcher.all') - - return { - 'success': not self.in_progress - } - - def searchAll(self): - - if self.in_progress: - log.info('Search already in progress') - fireEvent('notify.frontend', type = 'movie.searcher.already_started', data = True, message = 'Full search already in progress') - return - - self.in_progress = True - fireEvent('notify.frontend', type = 'movie.searcher.started', data = True, message = 'Full search started') - - db = get_db() - - medias = [x['_id'] for x in db.run('media', 'with_status', 'active', with_doc = False)] - random.shuffle(medias) - - total = len(medias) - self.in_progress = { - 'total': total, - 'to_go': total, - } - - try: - search_protocols = fireEvent('searcher.protocols', single = True) - - for media_id in medias: - - media = db.run('media', 'to_dict', media_id) - - try: - self.single(media, search_protocols) - except IndexError: - log.error('Forcing library update for %s, if you see this often, please report: %s', (media['identifier'], traceback.format_exc())) - fireEvent('movie.update_info', media_id) - except: - log.error('Search failed for %s: %s', (media['identifier'], traceback.format_exc())) - - self.in_progress['to_go'] -= 1 - - # Break if CP wants to shut down - if self.shuttingDown(): - break - - except SearchSetupError: - pass - - self.in_progress = False - - def single(self, movie, search_protocols = None, manual = False): - - # Find out search type - try: - if not search_protocols: - search_protocols = fireEvent('searcher.protocols', single = True) - except SearchSetupError: - return - - if not movie['profile_id'] or (movie['status'] == 'done' and not manual): - log.debug('Movie doesn\'t have a profile or already done, assuming in manage tab.') - return - - pre_releases = fireEvent('quality.pre_releases', single = True) - release_dates = fireEvent('movie.update_release_dates', movie['_id'], merge = True) - - found_releases = [] - too_early_to_search = [] - - default_title = getTitle(movie) - if not default_title: - log.error('No proper info found for movie, removing it from library to cause it from having more issues.') - fireEvent('media.delete', movie['_id'], single = True) - return - - fireEvent('notify.frontend', type = 'movie.searcher.started', data = {'_id': movie['_id']}, message = 'Searching for "%s"' % default_title) - - db = get_db() - - profile = db.get('id', movie['profile_id']) - quality_order = fireEvent('quality.order', single = True) - media_releases = db.run('release', 'for_media', movie['_id']) - - ret = False - - for q_identifier in profile.get('qualities'): - index = profile['qualities'].index(q_identifier) - quality_custom = { - 'quality': q_identifier, - 'finish': profile['finish'][index], - 'wait_for': profile['wait_for'][index] - } - - if not self.conf('always_search') and not self.couldBeReleased(q_identifier in pre_releases, release_dates, movie['info']['year']): - too_early_to_search.append(q_identifier) - continue - - has_better_quality = 0 - - # See if better quality is available - for release in media_releases: - if quality_order.index(release['quality']) <= quality_order.index(q_identifier) and release['status'] not in ['available', 'ignored', 'failed']: - has_better_quality += 1 - - # Don't search for quality lower then already available. - if has_better_quality is 0: - - quality = fireEvent('quality.single', identifier = q_identifier, single = True) - log.info('Search for %s in %s', (default_title, quality['label'])) - - results = fireEvent('searcher.search', search_protocols, movie, quality, single = True) or [] - if len(results) == 0: - log.debug('Nothing found for %s in %s', (default_title, quality['label'])) - - # Check if movie isn't deleted while searching - if not fireEvent('media.get', movie.get('_id'), single = True): - break - - # Add them to this movie releases list - found_releases += fireEvent('release.create_from_search', results, movie, quality, single = True) - - # Try find a valid result and download it - if fireEvent('release.try_download_result', results, movie, quality_custom, manual, single = True): - ret = True - - # Remove releases that aren't found anymore - for release in db.run('release', 'for_media', movie['_id']): - if release.get('status') == 'available' and release.get('identifier') not in found_releases: - fireEvent('release.delete', release.get('_id'), single = True) - - else: - log.info('Better quality (%s) already available or snatched for %s', (quality['label'], default_title)) - fireEvent('media.restatus', movie['_id']) - break - - # Break if CP wants to shut down - if self.shuttingDown() or ret: - break - - if len(too_early_to_search) > 0: - log.info2('Too early to search for %s, %s', (too_early_to_search, default_title)) - - fireEvent('notify.frontend', type = 'movie.searcher.ended', data = {'_id': movie['_id']}) - - return ret - - def correctRelease(self, nzb = None, media = None, quality = None, **kwargs): - - if media.get('type') != 'movie': return - - media_title = fireEvent('searcher.get_search_title', media, single = True) - - imdb_results = kwargs.get('imdb_results', False) - retention = Env.setting('retention', section = 'nzb') - - if nzb.get('seeders') is None and 0 < retention < nzb.get('age', 0): - log.info2('Wrong: Outside retention, age is %s, needs %s or lower: %s', (nzb['age'], retention, nzb['name'])) - return False - - # Check for required and ignored words - if not fireEvent('searcher.correct_words', nzb['name'], media, single = True): - return False - - preferred_quality = fireEvent('quality.single', identifier = quality['identifier'], single = True) - - # Contains lower quality string - if fireEvent('searcher.contains_other_quality', nzb, movie_year = media['info']['year'], preferred_quality = preferred_quality, single = True): - log.info2('Wrong: %s, looking for %s', (nzb['name'], quality['label'])) - return False - - - # File to small - if nzb['size'] and preferred_quality['size_min'] > nzb['size']: - log.info2('Wrong: "%s" is too small to be %s. %sMB instead of the minimal of %sMB.', (nzb['name'], preferred_quality['label'], nzb['size'], preferred_quality['size_min'])) - return False - - # File to large - if nzb['size'] and preferred_quality.get('size_max') < nzb['size']: - log.info2('Wrong: "%s" is too large to be %s. %sMB instead of the maximum of %sMB.', (nzb['name'], preferred_quality['label'], nzb['size'], preferred_quality['size_max'])) - return False - - - # Provider specific functions - get_more = nzb.get('get_more_info') - if get_more: - get_more(nzb) - - extra_check = nzb.get('extra_check') - if extra_check and not extra_check(nzb): - return False - - - if imdb_results: - return True - - # Check if nzb contains imdb link - if getImdb(nzb.get('description', '')) == media['identifier']: - return True - - for raw_title in media['info']['titles']: - for movie_title in possibleTitles(raw_title): - movie_words = re.split('\W+', simplifyString(movie_title)) - - if fireEvent('searcher.correct_name', nzb['name'], movie_title, single = True): - # if no IMDB link, at least check year range 1 - if len(movie_words) > 2 and fireEvent('searcher.correct_year', nzb['name'], media['info']['year'], 1, single = True): - return True - - # if no IMDB link, at least check year - if len(movie_words) <= 2 and fireEvent('searcher.correct_year', nzb['name'], media['info']['year'], 0, single = True): - return True - - log.info("Wrong: %s, undetermined naming. Looking for '%s (%s)'", (nzb['name'], media_title, media['info']['year'])) - return False - - def couldBeReleased(self, is_pre_release, dates, year = None): - - now = int(time.time()) - now_year = date.today().year - now_month = date.today().month - - if (year is None or year < now_year - 1) and (not dates or (dates.get('theater', 0) == 0 and dates.get('dvd', 0) == 0)): - return True - else: - - # Don't allow movies with years to far in the future - add_year = 1 if now_month > 10 else 0 # Only allow +1 year if end of the year - if year is not None and year > (now_year + add_year): - return False - - # For movies before 1972 - if not dates or dates.get('theater', 0) < 0 or dates.get('dvd', 0) < 0: - return True - - if is_pre_release: - # Prerelease 1 week before theaters - if dates.get('theater') - 604800 < now: - return True - else: - # 12 weeks after theater release - if dates.get('theater') > 0 and dates.get('theater') + 7257600 < now: - return True - - if dates.get('dvd') > 0: - - # 4 weeks before dvd release - if dates.get('dvd') - 2419200 < now: - return True - - # Dvd should be released - if dates.get('dvd') < now: - return True - - - return False - - def tryNextReleaseView(self, id = None, **kwargs): - - trynext = self.tryNextRelease(id, manual = True) - - return { - 'success': trynext - } - - def tryNextRelease(self, media_id, manual = False): - - try: - db = get_db() - rels = db.run('media', 'with_status', media_id, status = ['snatched', 'done']) - - for rel in rels: - rel['status'] = 'ignored' - db.update(rel) - - movie_dict = db.run('media', 'to_dict', media_id) - log.info('Trying next release for: %s', getTitle(movie_dict)) - self.single(movie_dict, manual = manual) - - return True - - except: - log.error('Failed searching for next release: %s', traceback.format_exc()) - return False - - def getSearchTitle(self, media): - if media['type'] == 'movie': - return getTitle(media) - -class SearchSetupError(Exception): - pass