diff --git a/gui/slick/images/banner.png b/gui/slick/images/banner.png index 8ae6c94eec5176b38fb7f895ca02612696fe8089..69f1f0a34fd22691f62bbd2fc4559814e1b9d886 100644 Binary files a/gui/slick/images/banner.png and b/gui/slick/images/banner.png differ diff --git a/gui/slick/images/flags/ar.png b/gui/slick/images/flags/ar.png index e5ef8f1fcddb9fa0b89c353430e9640c122445cc..0e5e5d261e87cfe5d1cf178250595ca254b68c27 100644 Binary files a/gui/slick/images/flags/ar.png and b/gui/slick/images/flags/ar.png differ diff --git a/gui/slick/images/flags/ay.png b/gui/slick/images/flags/ay.png new file mode 100644 index 0000000000000000000000000000000000000000..5958f6a3961cb7cf6adb1854ed1fd3cb3e38050a Binary files /dev/null and b/gui/slick/images/flags/ay.png differ diff --git a/gui/slick/images/flags/bg.png b/gui/slick/images/flags/bg.png index 0469f0607dc76eb60327c29e04d9585f3ef25dc7..7192aef517958f3062cbabf0cc2d7c4c68530ce6 100644 Binary files a/gui/slick/images/flags/bg.png and b/gui/slick/images/flags/bg.png differ diff --git a/gui/slick/images/flags/bs.png b/gui/slick/images/flags/bs.png index 639fa6cfa9c4792d03fb09fa197faf7ae549bfdf..05bb34e8f9ec5f3a330507daa1cbadc6ead569c8 100644 Binary files a/gui/slick/images/flags/bs.png and b/gui/slick/images/flags/bs.png differ diff --git a/gui/slick/images/flags/ca.png b/gui/slick/images/flags/ca.png index 1f204193ae58c87efdd88d46700ccb48c2e1d0d8..5d0305a80ed606e093e622d99e0c70bfff1262ff 100644 Binary files a/gui/slick/images/flags/ca.png and b/gui/slick/images/flags/ca.png differ diff --git a/gui/slick/images/flags/cs.png b/gui/slick/images/flags/cs.png index 8254790ca72f98d9e79d94bdfcb8839b1fd434ad..311ce520a1021b0799a40a1ed1cc8af9377e7845 100644 Binary files a/gui/slick/images/flags/cs.png and b/gui/slick/images/flags/cs.png differ diff --git a/gui/slick/images/flags/da.png b/gui/slick/images/flags/da.png index e2993d3c59ae78855f777c158a6aae6c1fb5c843..2c6dce00a532bbb3fa654373f15ee8bd62c0ef02 100644 Binary files a/gui/slick/images/flags/da.png and b/gui/slick/images/flags/da.png differ diff --git a/gui/slick/images/flags/de.png b/gui/slick/images/flags/de.png index ac4a977362738ca7daa20784717f10f9617136b4..8c52b0aa818347c9f0623646769e6145961526b4 100644 Binary files a/gui/slick/images/flags/de.png and b/gui/slick/images/flags/de.png differ diff --git a/gui/slick/images/flags/el.png b/gui/slick/images/flags/el.png index 8651ade7cbe030e85efc811a844d8f366c97a50c..5ae98285305309da2b707ec6ccbe58e5bbf5bd19 100644 Binary files a/gui/slick/images/flags/el.png and b/gui/slick/images/flags/el.png differ diff --git a/gui/slick/images/flags/en.png b/gui/slick/images/flags/en.png index 10f451fe85c41c6c9a06d543a57114ae2f87ecc1..ba72777157ff3cdb900aaf0fb0de6aeb8c627ba5 100644 Binary files a/gui/slick/images/flags/en.png and b/gui/slick/images/flags/en.png differ diff --git a/gui/slick/images/flags/eo.png b/gui/slick/images/flags/eo.png new file mode 100644 index 0000000000000000000000000000000000000000..9b861cde145b1b2cb41cf7e17502991b9833e46b Binary files /dev/null and b/gui/slick/images/flags/eo.png differ diff --git a/gui/slick/images/flags/es.png b/gui/slick/images/flags/es.png index c2de2d7111e3cb59cf6511dd2ab045e824bdb43e..3f755e5e93902b8321a3fde21cfe0de3c53dae4f 100644 Binary files a/gui/slick/images/flags/es.png and b/gui/slick/images/flags/es.png differ diff --git a/gui/slick/images/flags/et.png b/gui/slick/images/flags/et.png index 2e893fa056c3d27448b6b9b6579486439ac6e490..d510e7fc26363c08453390243aeb215f00fb2b88 100644 Binary files a/gui/slick/images/flags/et.png and b/gui/slick/images/flags/et.png differ diff --git a/gui/slick/images/flags/fa.png b/gui/slick/images/flags/fa.png new file mode 100644 index 0000000000000000000000000000000000000000..9a92b576dbaf11d5ea9ed1261149a232a1f1e842 Binary files /dev/null and b/gui/slick/images/flags/fa.png differ diff --git a/gui/slick/images/flags/fi.png b/gui/slick/images/flags/fi.png index 14ec091b802cf24ebd9f8825f81cd2f6e360b46d..4f74d8b2ad17537e8acdcca6ba18e4bbfd219ea7 100644 Binary files a/gui/slick/images/flags/fi.png and b/gui/slick/images/flags/fi.png differ diff --git a/gui/slick/images/flags/fr.png b/gui/slick/images/flags/fr.png index 8332c4ec23c853944c29b02d7b32a88033f48a71..cec29aac0016d5e54017364c3df8bb8c359253d0 100644 Binary files a/gui/slick/images/flags/fr.png and b/gui/slick/images/flags/fr.png differ diff --git a/gui/slick/images/flags/gl.png b/gui/slick/images/flags/gl.png index ef12a73bf9628ff5a67b81bd980d9c5d2b2c0f05..ca60f6a2ede0b469dd2b6fcf121bdcc51914cadc 100644 Binary files a/gui/slick/images/flags/gl.png and b/gui/slick/images/flags/gl.png differ diff --git a/gui/slick/images/flags/he.png b/gui/slick/images/flags/he.png index 31b61bc7a7345cddeecfb0790d3898a1539d6314..0800eb9840fecc68fe5636163a5bf36ce20cdf2d 100644 Binary files a/gui/slick/images/flags/he.png and b/gui/slick/images/flags/he.png differ diff --git a/gui/slick/images/flags/hi.png b/gui/slick/images/flags/hi.png new file mode 100644 index 0000000000000000000000000000000000000000..a775c6d4ac2a7cb6ad00ebee42b136029086fe43 Binary files /dev/null and b/gui/slick/images/flags/hi.png differ diff --git a/gui/slick/images/flags/hr.png b/gui/slick/images/flags/hr.png index 696b515460ddb670acb7e9de4438aaf21fc5fb77..1f07f09a8fa5ade821f215c40bd34da7216eef92 100644 Binary files a/gui/slick/images/flags/hr.png and b/gui/slick/images/flags/hr.png differ diff --git a/gui/slick/images/flags/hu.png b/gui/slick/images/flags/hu.png index 7baafe44ddcaec29ad9f187f759a7fa3a1a5df00..d5cbe53c807137cf7fe70686990a82fe1b7ca824 100644 Binary files a/gui/slick/images/flags/hu.png and b/gui/slick/images/flags/hu.png differ diff --git a/gui/slick/images/flags/hy.png b/gui/slick/images/flags/hy.png new file mode 100644 index 0000000000000000000000000000000000000000..f80bcd20d73b75b43f35f1ded11ec0766da4e355 Binary files /dev/null and b/gui/slick/images/flags/hy.png differ diff --git a/gui/slick/images/flags/id.png b/gui/slick/images/flags/id.png index c6bc0fafac79403c97c64ba0228d35f250d05b57..e651f2745db84d4a5dbb952c4e5c24bf639b5cff 100644 Binary files a/gui/slick/images/flags/id.png and b/gui/slick/images/flags/id.png differ diff --git a/gui/slick/images/flags/is.png b/gui/slick/images/flags/is.png index b8f6d0f06675a9570c2c6e696ee51282097c3876..8e3696e0446cc1bee90789ac6d5cbac381c3956c 100644 Binary files a/gui/slick/images/flags/is.png and b/gui/slick/images/flags/is.png differ diff --git a/gui/slick/images/flags/it.png b/gui/slick/images/flags/it.png index 89692f74f051cd43503744c3dab65c8ba773b7e2..b9d078d7efb727e72d6e537b87e4c17fe6ecfe4a 100644 Binary files a/gui/slick/images/flags/it.png and b/gui/slick/images/flags/it.png differ diff --git a/gui/slick/images/flags/ja.png b/gui/slick/images/flags/ja.png index 4fbc123814ef5e2b2af77e93591ee53715954319..454b6d64111f98fd55e972ab5bcf22398802b546 100644 Binary files a/gui/slick/images/flags/ja.png and b/gui/slick/images/flags/ja.png differ diff --git a/gui/slick/images/flags/ka.png b/gui/slick/images/flags/ka.png new file mode 100644 index 0000000000000000000000000000000000000000..1d31854ebf0762c4d18ba6fb8ea78138b3ba924f Binary files /dev/null and b/gui/slick/images/flags/ka.png differ diff --git a/gui/slick/images/flags/kk.png b/gui/slick/images/flags/kk.png new file mode 100644 index 0000000000000000000000000000000000000000..02780e3de9c80f64a2add48bdd0a70df46110cc1 Binary files /dev/null and b/gui/slick/images/flags/kk.png differ diff --git a/gui/slick/images/flags/ko.png b/gui/slick/images/flags/ko.png index c1179b53a0e7dc3b8c66df4856484df009959117..7b7b6b9345fd3698d27c5dd8b81e8ea34030ba49 100644 Binary files a/gui/slick/images/flags/ko.png and b/gui/slick/images/flags/ko.png differ diff --git a/gui/slick/images/flags/lb.png b/gui/slick/images/flags/lb.png index d0d452bf868e0cd6417f518f1dbe695f191ef392..d19b0de4f3c6ed7adb3928207b3b184f29c15f20 100644 Binary files a/gui/slick/images/flags/lb.png and b/gui/slick/images/flags/lb.png differ diff --git a/gui/slick/images/flags/lt.png b/gui/slick/images/flags/lt.png index c8ef0da0919b1e77ca91232de0cdf0d99dc8d68f..534d28ce4ac9c6941b8e21791a8b2c9cb5cc9057 100644 Binary files a/gui/slick/images/flags/lt.png and b/gui/slick/images/flags/lt.png differ diff --git a/gui/slick/images/flags/lv.png b/gui/slick/images/flags/lv.png index 49b69981085ff54568907cd51a56a1e5d8b01ada..0b2d3cbb57462d6e00628ffe9e7b08938ed3df0e 100644 Binary files a/gui/slick/images/flags/lv.png and b/gui/slick/images/flags/lv.png differ diff --git a/gui/slick/images/flags/mk.png b/gui/slick/images/flags/mk.png index db173aaff21955d9aed640beb344986335a1d164..30384347c8ee71d4521876cd7663720a3fd91d50 100644 Binary files a/gui/slick/images/flags/mk.png and b/gui/slick/images/flags/mk.png differ diff --git a/gui/slick/images/flags/ms.png b/gui/slick/images/flags/ms.png index d4cbb433d8f9fe49f06585dc46ee15593e3e621c..9572ba3d0887bf53b09a327b4bd8f37b568d785b 100644 Binary files a/gui/slick/images/flags/ms.png and b/gui/slick/images/flags/ms.png differ diff --git a/gui/slick/images/flags/nl.png b/gui/slick/images/flags/nl.png index fe44791e32b790949b0317ab3c258864b9024ebe..cf9797eb90aebfeaafb31e18cb9a717cd29989c4 100644 Binary files a/gui/slick/images/flags/nl.png and b/gui/slick/images/flags/nl.png differ diff --git a/gui/slick/images/flags/no.png b/gui/slick/images/flags/no.png index 160b6b5b79db15e623fa55e5774e5d160b933180..cdc0d5134b738d2b733544ee0c35572cef38028c 100644 Binary files a/gui/slick/images/flags/no.png and b/gui/slick/images/flags/no.png differ diff --git a/gui/slick/images/flags/oc.png b/gui/slick/images/flags/oc.png new file mode 100644 index 0000000000000000000000000000000000000000..a589445fdb103f873b162e9f6d376ab29bb0b3ed Binary files /dev/null and b/gui/slick/images/flags/oc.png differ diff --git a/gui/slick/images/flags/pb.png b/gui/slick/images/flags/pb.png index 9b1a5538b264a295021f4f717d4299bb8ed98d98..2fecf539d9ddd9b191efdd0eae3003a1d2c2e37a 100644 Binary files a/gui/slick/images/flags/pb.png and b/gui/slick/images/flags/pb.png differ diff --git a/gui/slick/images/flags/pl.png b/gui/slick/images/flags/pl.png index d413d010b5b097c4e0a4604eba86dad79567ed16..80c75ecf241fc61e0ad16576aaeda80b2f8e0e4e 100644 Binary files a/gui/slick/images/flags/pl.png and b/gui/slick/images/flags/pl.png differ diff --git a/gui/slick/images/flags/pt.png b/gui/slick/images/flags/pt.png index ece79801506ecf8c42397349b4fa2cfe8176b999..56adb3015c4b0de73a1b053972a0f3635f741cd7 100644 Binary files a/gui/slick/images/flags/pt.png and b/gui/slick/images/flags/pt.png differ diff --git a/gui/slick/images/flags/ro.png b/gui/slick/images/flags/ro.png index 57e74a6510dd6a4b29668db181cb94727d1eb4b7..5b0840b4fb374cb9255d22725566de5a8db4ac68 100644 Binary files a/gui/slick/images/flags/ro.png and b/gui/slick/images/flags/ro.png differ diff --git a/gui/slick/images/flags/ru.png b/gui/slick/images/flags/ru.png index 47da4214fd9edb383687c1d4f84fe8b42a51ceb2..75b4034ebc1ff0089048d5406883347eb11cb4d5 100644 Binary files a/gui/slick/images/flags/ru.png and b/gui/slick/images/flags/ru.png differ diff --git a/gui/slick/images/flags/si.png b/gui/slick/images/flags/si.png index bb1476ff5fe8e0d3af4fc6bd11e513d95fd9cccd..f8a8ebc21442eb677f6ee4353c6a507195323396 100644 Binary files a/gui/slick/images/flags/si.png and b/gui/slick/images/flags/si.png differ diff --git a/gui/slick/images/flags/sk.png b/gui/slick/images/flags/sk.png index 7ccbc8274ad8f76f28960b83f2bba2a619029d87..0da9e298f66d9a7143d6c3ffb7f1252c6dc380ca 100644 Binary files a/gui/slick/images/flags/sk.png and b/gui/slick/images/flags/sk.png differ diff --git a/gui/slick/images/flags/sl.png b/gui/slick/images/flags/sl.png index 12d812d29fa6ea097743074e4a341ccfc691946a..733347a9c88ed5029579b41e91cf141eb1fd21d7 100644 Binary files a/gui/slick/images/flags/sl.png and b/gui/slick/images/flags/sl.png differ diff --git a/gui/slick/images/flags/sq.png b/gui/slick/images/flags/sq.png new file mode 100644 index 0000000000000000000000000000000000000000..67e629ce2dd931f2eac8fa89179a67eb2b7a0a77 Binary files /dev/null and b/gui/slick/images/flags/sq.png differ diff --git a/gui/slick/images/flags/sr.png b/gui/slick/images/flags/sr.png index 5eff9271d28cf8bf1cb85378600c4fa4997faa33..02aa99a42944791696a9f99589f8000eb96b8ee1 100644 Binary files a/gui/slick/images/flags/sr.png and b/gui/slick/images/flags/sr.png differ diff --git a/gui/slick/images/flags/sv.png b/gui/slick/images/flags/sv.png index 1994653dac1fc1c6ee3c9fcb35c8af97f16eefc7..aed90f2dbb04d668ca6f72dc2b1788ab0edd81fa 100644 Binary files a/gui/slick/images/flags/sv.png and b/gui/slick/images/flags/sv.png differ diff --git a/gui/slick/images/flags/th.png b/gui/slick/images/flags/th.png index dd8ba91719ba641502bc7ffda16c25dc71b2066c..758d45fcbc6a9e5af79c0100ec6ae72cd5e11219 100644 Binary files a/gui/slick/images/flags/th.png and b/gui/slick/images/flags/th.png differ diff --git a/gui/slick/images/flags/tl.png b/gui/slick/images/flags/tl.png index 77da181e9c57a490c90a99ec08a8718ea8fc0835..ea414aabdf737b1e01e930aabd692ab125b8e7da 100644 Binary files a/gui/slick/images/flags/tl.png and b/gui/slick/images/flags/tl.png differ diff --git a/gui/slick/images/flags/tr.png b/gui/slick/images/flags/tr.png index be32f77e9910c0896c1ee8e7ed4f0edf815a517e..bfcdca3e46a644c164870dfd0cb76eb04ecf489e 100644 Binary files a/gui/slick/images/flags/tr.png and b/gui/slick/images/flags/tr.png differ diff --git a/gui/slick/images/flags/uk.png b/gui/slick/images/flags/uk.png new file mode 100644 index 0000000000000000000000000000000000000000..6c3b3fb43215b0d3b5cbcf196a6e03a3f7d9a05c Binary files /dev/null and b/gui/slick/images/flags/uk.png differ diff --git a/gui/slick/images/flags/un.png b/gui/slick/images/flags/un.png new file mode 100644 index 0000000000000000000000000000000000000000..035b8b99f7fce7f95ce460653dd1a0d6ac091b7c Binary files /dev/null and b/gui/slick/images/flags/un.png differ diff --git a/gui/slick/images/flags/unknown.png b/gui/slick/images/flags/unknown.png index 41ab4e6ef283765d69c3d2598910710e15449d45..af9249bc317b724a8923e1447c60977dc9a91827 100644 Binary files a/gui/slick/images/flags/unknown.png and b/gui/slick/images/flags/unknown.png differ diff --git a/gui/slick/images/flags/vi.png b/gui/slick/images/flags/vi.png index ed26915a3238534bf8f1249b75dd9ddde10db65a..20e00c9b3508eedc0ae3c8a0075be5ac6373603e 100644 Binary files a/gui/slick/images/flags/vi.png and b/gui/slick/images/flags/vi.png differ diff --git a/gui/slick/images/flags/zh.png b/gui/slick/images/flags/zh.png index 89144146219e6fbec7eaa89e1bf4b073d299569e..4f12a8461c08478c032e84bbe8e442d1aa9f316b 100644 Binary files a/gui/slick/images/flags/zh.png and b/gui/slick/images/flags/zh.png differ diff --git a/gui/slick/images/poster.png b/gui/slick/images/poster.png index 906fb562870499fd5cde7c8d1c605dbcb0b4d337..c280c1849da45425aac8b977920be978ec6381b4 100644 Binary files a/gui/slick/images/poster.png and b/gui/slick/images/poster.png differ diff --git a/gui/slick/interfaces/default/config_general.tmpl b/gui/slick/interfaces/default/config_general.tmpl index b098c75ef5387c9d50dd125a09a3cd6c76629bd8..a16527b473adab106b0bd791c8fd06f7db6ecdd1 100644 --- a/gui/slick/interfaces/default/config_general.tmpl +++ b/gui/slick/interfaces/default/config_general.tmpl @@ -317,16 +317,6 @@ <fieldset class="component-group-list"> - <div class="field-pair"> - <label for="use_api"> - <span class="component-title">Enable API</span> - <span class="component-desc"> - <input type="checkbox" name="use_api" class="enabler" id="use_api" #if $sickbeard.USE_API then 'checked="checked"' else ''#/> - <p>allow the use of the SickRage API</p> - </span> - </label> - </div> - <div id="content_use_api"> <div class="field-pair"> <label for="api_key"> <span class="component-title">API key</span> @@ -337,7 +327,6 @@ </span> </label> </div> - </div> <div class="field-pair"> <label for="web_log"> @@ -581,10 +570,10 @@ <div class="field-pair"> <label for="git_reset"> - <span class="component-title">Git branch reset</span> + <span class="component-title">Git reset</span> <span class="component-desc"> <input type="checkbox" name="git_reset" id="git_reset" #if True == $sickbeard.GIT_RESET then 'checked="checked"' else ''#/> - <p>reset git branch automatically to help resolve update issues</p> + <p>removes untracked files and performs a hard reset on git branch automatically to help resolve update issues</p> </span> </label> </div> diff --git a/gui/slick/interfaces/default/displayShow.tmpl b/gui/slick/interfaces/default/displayShow.tmpl index f2bfb9a7641b8d1e5477de98f5f539c9c160dc26..26268fd4ebad35bc1f92e36ef7579aa0c276848b 100644 --- a/gui/slick/interfaces/default/displayShow.tmpl +++ b/gui/slick/interfaces/default/displayShow.tmpl @@ -474,7 +474,7 @@ #if $sickbeard.USE_SUBTITLES and $show.subtitles: <td class="col-subtitles" align="center"> #if $epResult["subtitles"]: - #for $sub_lang in subliminal.language.language_list($epResult["subtitles"].split(',')): + #for $sub_lang in subliminal.language.language_list([x.strip() for x in $epResult["subtitles"].split(',')]): #if sub_lang.alpha2 != "" <img src="$sbRoot/images/flags/${sub_lang.alpha2}.png" width="16" height="11" alt="${sub_lang}" /> #end if diff --git a/gui/slick/interfaces/default/editShow.tmpl b/gui/slick/interfaces/default/editShow.tmpl index c031ec13cc2f63392294b42268af12c12521f4dd..ef959c6b26b1b4238803b2d5212a2a90c3a0523a 100644 --- a/gui/slick/interfaces/default/editShow.tmpl +++ b/gui/slick/interfaces/default/editShow.tmpl @@ -133,7 +133,7 @@ <b>Sports: </b> <input type="checkbox" name="sports" #if $show.sports == 1 then "checked=\"checked\"" else ""# /><br /> -(check this if the show is a sporting or MMA event)<br /> +(check this if the show is a sporting or MMA event and released as Show.03.02.2010 rather than Show.S02E03)<br /> <br /> <b>Anime: </b> diff --git a/gui/slick/interfaces/default/home.tmpl b/gui/slick/interfaces/default/home.tmpl index 12cf6a9597c71ac8134bbd0f5720b6455c8b5636..ae9d6bba29fd5d40024e750335b6b647e177d331 100644 --- a/gui/slick/interfaces/default/home.tmpl +++ b/gui/slick/interfaces/default/home.tmpl @@ -1,4 +1,5 @@ #import sickbeard +#import calendar #import datetime #from sickbeard.common import * #from sickbeard import db, sbdatetime, network_timezones @@ -330,7 +331,7 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name)) #set $data_date = '6000000000.0' #if $cur_airs_next: - #set $data_date = $time.mktime($sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network)).timetuple()) + #set $data_date = $calendar.timegm($sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network)).timetuple()) #else if None is not $display_status #if 'nded' not in $display_status and 1 == int($curShow.paused) #set $data_date = '5000000500.0' @@ -528,7 +529,7 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name)) #if $cur_airs_next #set $ldatetime = $sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network)) - <td align="center" class="nowrap"><div class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdate($ldatetime)</div><span class="sort_data">$time.mktime($ldatetime.timetuple())</span></td> + <td align="center" class="nowrap"><div class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdate($ldatetime)</div><span class="sort_data">$calendar.timegm($ldatetime.timetuple())</span></td> #else: <td align="center" class="nowrap"></td> #end if diff --git a/lib/fanart/__init__.py b/lib/fanart/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8acc7f5cd6034d54c8e83e1dc037bed25a2479bb --- /dev/null +++ b/lib/fanart/__init__.py @@ -0,0 +1,111 @@ +__author__ = 'Andrea De Marco <24erre@gmail.com>' +__version__ = '1.4.0' +__classifiers__ = [ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Software Development :: Libraries', +] +__copyright__ = "2012, %s " % __author__ +__license__ = """ + Copyright %s. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" % __copyright__ + +__docformat__ = 'restructuredtext en' + +__doc__ = """ +:abstract: Python interface to fanart.tv API +:version: %s +:author: %s +:contact: http://z4r.github.com/ +:date: 2012-04-04 +:copyright: %s +""" % (__version__, __author__, __license__) + + +def values(obj): + return [v for k, v in obj.__dict__.iteritems() if not k.startswith('_')] + +BASEURL = 'http://webservice.fanart.tv/v3/%s/%s?api_key=%s' + +class FORMAT(object): + JSON = 'JSON' + XML = 'XML' + PHP = 'PHP' + + +class WS(object): + MUSIC = 'music' + MOVIE = 'movies' + TV = 'tv' + + +class TYPE(object): + ALL = 'all' + + class TV(object): + LOGO = 'clearlogo' + CHARACTER = 'characterart' + BACKGROUND = 'showbackground' + HDLOGO = 'hdtvlogo' + HDART = 'hdclearart' + ART = 'clearart' + THUMB = 'tvthumb' + POSTER = 'tvposter' + BANNER = 'tvbanner' + SEASONTHUMB = 'seasonthumb' + SEASONPOSTER = 'seasonposter' + SEASONBANNER = 'seasonbanner' + + class MUSIC(object): + DISC = 'cdart' + LOGO = 'musiclogo' + BACKGROUND = 'artistbackground' + COVER = 'albumcover' + THUMB = 'artistthumb' + + class MOVIE(object): + ART = 'movieart' + LOGO = 'movielogo' + DISC = 'moviedisc' + POSTER = 'movieposter' + BACKGROUND = 'moviebackground' + HDLOGO = 'hdmovielogo' + HDART = 'hdmovieclearart' + BANNER = 'moviebanner' + THUMB = 'moviethumb' + + +class SORT(object): + POPULAR = 1 + NEWEST = 2 + OLDEST = 3 + + +class LIMIT(object): + ONE = 1 + ALL = 2 + +FORMAT_LIST = values(FORMAT) +WS_LIST = values(WS) +TYPE_LIST = values(TYPE.MUSIC) + values(TYPE.TV) + values(TYPE.MOVIE) + [TYPE.ALL] +MUSIC_TYPE_LIST = values(TYPE.MUSIC) + [TYPE.ALL] +TV_TYPE_LIST = values(TYPE.TV) + [TYPE.ALL] +MOVIE_TYPE_LIST = values(TYPE.MOVIE) + [TYPE.ALL] +SORT_LIST = values(SORT) +LIMIT_LIST = values(LIMIT) diff --git a/lib/fanart/core.py b/lib/fanart/core.py new file mode 100644 index 0000000000000000000000000000000000000000..6b3af96d468a04db16daa19e7be25eb1bc08b5b5 --- /dev/null +++ b/lib/fanart/core.py @@ -0,0 +1,35 @@ +import requests +import fanart +from fanart.errors import RequestFanartError, ResponseFanartError + + +class Request(object): + def __init__(self, apikey, id, ws, type=None, sort=None, limit=None): + self._apikey = apikey + self._id = id + self._ws = ws + self._type = type or fanart.TYPE.ALL + self._sort = sort or fanart.SORT.POPULAR + self._limit = limit or fanart.LIMIT.ALL + self.validate() + self._response = None + + def validate(self): + for attribute_name in ('ws', 'type', 'sort', 'limit'): + attribute = getattr(self, '_' + attribute_name) + choices = getattr(fanart, attribute_name.upper() + '_LIST') + if attribute not in choices: + raise RequestFanartError('Not allowed {0}: {1} [{2}]'.format(attribute_name, attribute, ', '.join(choices))) + + def __str__(self): + return fanart.BASEURL % (self._ws, self._id, self._apikey) + + def response(self): + try: + response = requests.get(str(self)) + rjson = response.json() + if not isinstance(rjson, dict): + raise Exception(response.text) + return rjson + except Exception as e: + raise ResponseFanartError(str(e)) diff --git a/lib/fanart/errors.py b/lib/fanart/errors.py new file mode 100644 index 0000000000000000000000000000000000000000..95a71e35ed58893ca1f74677d1392179e59b1901 --- /dev/null +++ b/lib/fanart/errors.py @@ -0,0 +1,15 @@ +class FanartError(Exception): + def __str__(self): + return ', '.join(map(str, self.args)) + + def __repr__(self): + name = self.__class__.__name__ + return '%s%r' % (name, self.args) + + +class ResponseFanartError(FanartError): + pass + + +class RequestFanartError(FanartError): + pass diff --git a/lib/fanart/immutable.py b/lib/fanart/immutable.py new file mode 100644 index 0000000000000000000000000000000000000000..170de37086eab8dfde19097edaf31c5e6dbd42b9 --- /dev/null +++ b/lib/fanart/immutable.py @@ -0,0 +1,46 @@ +class Immutable(object): + _mutable = False + + def __setattr__(self, name, value): + if self._mutable or name == '_mutable': + super(Immutable, self).__setattr__(name, value) + else: + raise TypeError("Can't modify immutable instance") + + def __delattr__(self, name): + if self._mutable: + super(Immutable, self).__delattr__(name) + else: + raise TypeError("Can't modify immutable instance") + + def __eq__(self, other): + return hash(self) == hash(other) + + def __hash__(self): + return hash(repr(self)) + + def __repr__(self): + return '%s(%s)' % ( + self.__class__.__name__, + ', '.join(['{0}={1}'.format(k, repr(v)) for k, v in self]) + ) + + def __iter__(self): + l = self.__dict__.keys() + l.sort() + for k in l: + if not k.startswith('_'): + yield k, getattr(self, k) + + @staticmethod + def mutablemethod(f): + def func(self, *args, **kwargs): + if isinstance(self, Immutable): + old_mutable = self._mutable + self._mutable = True + res = f(self, *args, **kwargs) + self._mutable = old_mutable + else: + res = f(self, *args, **kwargs) + return res + return func diff --git a/lib/fanart/items.py b/lib/fanart/items.py new file mode 100644 index 0000000000000000000000000000000000000000..778e1a1b2ba5e936061deb03e05423468277f4c3 --- /dev/null +++ b/lib/fanart/items.py @@ -0,0 +1,68 @@ +import json +import os +import requests +from fanart.core import Request +from fanart.immutable import Immutable + + +class LeafItem(Immutable): + KEY = NotImplemented + + @Immutable.mutablemethod + def __init__(self, id, url, likes): + self.id = int(id) + self.url = url + self.likes = int(likes) + self._content = None + + @classmethod + def from_dict(cls, resource): + return cls(**dict([(str(k), v) for k, v in resource.iteritems()])) + + @classmethod + def extract(cls, resource): + return [cls.from_dict(i) for i in resource.get(cls.KEY, {})] + + @Immutable.mutablemethod + def content(self): + if not self._content: + self._content = requests.get(self.url).content + return self._content + + def __str__(self): + return self.url + + +class ResourceItem(Immutable): + WS = NotImplemented + request_cls = Request + + @classmethod + def from_dict(cls, map): + raise NotImplementedError + + @classmethod + def get(cls, id): + map = cls.request_cls( + apikey=os.environ.get('FANART_APIKEY'), + id=id, + ws=cls.WS + ).response() + return cls.from_dict(map) + + def json(self, **kw): + return json.dumps( + self, + default=lambda o: dict([(k, v) for k, v in o.__dict__.items() if not k.startswith('_')]), + **kw + ) + + +class CollectableItem(Immutable): + @classmethod + def from_dict(cls, key, map): + raise NotImplementedError + + @classmethod + def collection_from_dict(cls, map): + return [cls.from_dict(k, v) for k, v in map.iteritems()] diff --git a/lib/fanart/movie.py b/lib/fanart/movie.py new file mode 100644 index 0000000000000000000000000000000000000000..c69e860b1ee458654c0aebc9647619f910826891 --- /dev/null +++ b/lib/fanart/movie.py @@ -0,0 +1,103 @@ +import fanart +from fanart.items import LeafItem, Immutable, ResourceItem +__all__ = ( + 'ArtItem', + 'DiscItem', + 'LogoItem', + 'PosterItem', + 'BackgroundItem', + 'HdLogoItem', + 'HdArtItem', + 'BannerItem', + 'ThumbItem', + 'Movie', +) + + +class MovieItem(LeafItem): + + @Immutable.mutablemethod + def __init__(self, id, url, likes, lang): + super(MovieItem, self).__init__(id, url, likes) + self.lang = lang + + +class DiscItem(MovieItem): + KEY = fanart.TYPE.MOVIE.DISC + + @Immutable.mutablemethod + def __init__(self, id, url, likes, lang, disc, disc_type): + super(DiscItem, self).__init__(id, url, likes, lang) + self.disc = int(disc) + self.disc_type = disc_type + + +class ArtItem(MovieItem): + KEY = fanart.TYPE.MOVIE.ART + + +class LogoItem(MovieItem): + KEY = fanart.TYPE.MOVIE.LOGO + + +class PosterItem(MovieItem): + KEY = fanart.TYPE.MOVIE.POSTER + + +class BackgroundItem(MovieItem): + KEY = fanart.TYPE.MOVIE.BACKGROUND + + +class HdLogoItem(MovieItem): + KEY = fanart.TYPE.MOVIE.HDLOGO + + +class HdArtItem(MovieItem): + KEY = fanart.TYPE.MOVIE.HDART + + +class BannerItem(MovieItem): + KEY = fanart.TYPE.MOVIE.BANNER + + +class ThumbItem(MovieItem): + KEY = fanart.TYPE.MOVIE.THUMB + + +class Movie(ResourceItem): + WS = fanart.WS.MOVIE + + @Immutable.mutablemethod + def __init__(self, name, imdbid, tmdbid, arts, logos, discs, posters, backgrounds, hdlogos, hdarts, + banners, thumbs): + self.name = name + self.imdbid = imdbid + self.tmdbid = tmdbid + self.arts = arts + self.posters = posters + self.logos = logos + self.discs = discs + self.backgrounds = backgrounds + self.hdlogos = hdlogos + self.hdarts = hdarts + self.banners = banners + self.thumbs = thumbs + + @classmethod + def from_dict(cls, resource): + assert len(resource) == 1, 'Bad Format Map' + name, resource = resource.items()[0] + return cls( + name=name, + imdbid=resource['imdb_id'], + tmdbid=resource['tmdb_id'], + arts=ArtItem.extract(resource), + logos=LogoItem.extract(resource), + discs=DiscItem.extract(resource), + posters=PosterItem.extract(resource), + backgrounds=BackgroundItem.extract(resource), + hdlogos=HdLogoItem.extract(resource), + hdarts=HdArtItem.extract(resource), + banners=BannerItem.extract(resource), + thumbs=ThumbItem.extract(resource), + ) diff --git a/lib/fanart/music.py b/lib/fanart/music.py new file mode 100644 index 0000000000000000000000000000000000000000..9f10309373975f45a81806d764964791b6474ca1 --- /dev/null +++ b/lib/fanart/music.py @@ -0,0 +1,80 @@ +from fanart.items import Immutable, LeafItem, ResourceItem, CollectableItem +import fanart +__all__ = ( + 'BackgroundItem', + 'CoverItem', + 'LogoItem', + 'ThumbItem', + 'DiscItem', + 'Artist', + 'Album', +) + + +class BackgroundItem(LeafItem): + KEY = fanart.TYPE.MUSIC.BACKGROUND + + +class CoverItem(LeafItem): + KEY = fanart.TYPE.MUSIC.COVER + + +class LogoItem(LeafItem): + KEY = fanart.TYPE.MUSIC.LOGO + + +class ThumbItem(LeafItem): + KEY = fanart.TYPE.MUSIC.THUMB + + +class DiscItem(LeafItem): + KEY = fanart.TYPE.MUSIC.DISC + + @Immutable.mutablemethod + def __init__(self, id, url, likes, disc, size): + super(DiscItem, self).__init__(id, url, likes) + self.disc = int(disc) + self.size = int(size) + + +class Artist(ResourceItem): + WS = fanart.WS.MUSIC + + @Immutable.mutablemethod + def __init__(self, name, mbid, albums, backgrounds, logos, thumbs): + self.name = name + self.mbid = mbid + self.albums = albums + self.backgrounds = backgrounds + self.logos = logos + self.thumbs = thumbs + + @classmethod + def from_dict(cls, resource): + assert len(resource) == 1, 'Bad Format Map' + name, resource = resource.items()[0] + return cls( + name=name, + mbid=resource['mbid_id'], + albums=Album.collection_from_dict(resource.get('albums', {})), + backgrounds=BackgroundItem.extract(resource), + thumbs=ThumbItem.extract(resource), + logos=LogoItem.extract(resource), + ) + + +class Album(CollectableItem): + + @Immutable.mutablemethod + def __init__(self, mbid, covers, arts): + self.mbid = mbid + self.covers = covers + self.arts = arts + + @classmethod + def from_dict(cls, key, resource): + return cls( + mbid=key, + covers=CoverItem.extract(resource), + arts=DiscItem.extract(resource), + ) diff --git a/lib/fanart/tests/__init__.py b/lib/fanart/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..957cbe388c57468295787d5cde0dad6b381dffbf --- /dev/null +++ b/lib/fanart/tests/__init__.py @@ -0,0 +1,3 @@ +import os + +LOCALDIR = os.path.dirname(__file__) diff --git a/lib/fanart/tests/json/wilfred.json b/lib/fanart/tests/json/wilfred.json new file mode 100644 index 0000000000000000000000000000000000000000..2065f9cfe054c6c6da717405fa0fc2fb9d33252e --- /dev/null +++ b/lib/fanart/tests/json/wilfred.json @@ -0,0 +1,196 @@ +{ + "logos": [ + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/clearlogo/wilfred-us-4e04b6495dfd3.png", + "likes": 2, + "id": 11977 + }, + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/clearlogo/wilfred-us-517ac36e39f67.png", + "likes": 1, + "id": 28249 + }, + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/clearlogo/wilfred-us-51f557082cfde.png", + "likes": 0, + "id": 31817 + } + ], + "arts": [ + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/clearart/wilfred-us-4e05f10e87711.png", + "likes": 2, + "id": 11987 + }, + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/clearart/wilfred-us-4e2f151d5ed62.png", + "likes": 1, + "id": 12470 + } + ], + "name": "Wilfred (US)", + "hdarts": [ + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/hdclearart/wilfred-us-505f94ed0ba13.png", + "likes": 1, + "id": 21112 + }, + { + "lang": "he", + "url": "http://assets.fanart.tv/fanart/tv/239761/hdclearart/wilfred-us-52403264aa3ec.png", + "likes": 1, + "id": 33751 + } + ], + "backgrounds": [ + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-5034dbd49115e.jpg", + "id": 19965, + "season": 0, + "likes": 0 + }, + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-50b0c92db6973.jpg", + "id": 23166, + "season": 0, + "likes": 0 + }, + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-50b0c92dbb46b.jpg", + "id": 23167, + "season": 0, + "likes": 0 + }, + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-50b0c92dbb9d1.jpg", + "id": 23168, + "season": 0, + "likes": 0 + } + ], + "thumbs": [ + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/tvthumb/wilfred-us-501cf526174fe.jpg", + "likes": 1, + "id": 19596 + }, + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/tvthumb/wilfred-us-51bfb4a105904.jpg", + "likes": 0, + "id": 30060 + } + ], + "characters": [], + "posters": [ + { + "lang": "he", + "url": "http://assets.fanart.tv/fanart/tv/239761/tvposter/wilfred-us-525d893230d7c.jpg", + "likes": 1, + "id": 34584 + } + ], + "seasons": [ + { + "lang": "he", + "url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-52403782bab55.jpg", + "id": 33752, + "season": 1, + "likes": 1 + }, + { + "lang": "he", + "url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-5240379335232.jpg", + "id": 33753, + "season": 2, + "likes": 1 + }, + { + "lang": "he", + "url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-524037bc83c7d.jpg", + "id": 33754, + "season": 3, + "likes": 1 + }, + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-501bb0a8e60f9.jpg", + "id": 19586, + "season": 1, + "likes": 0 + }, + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-501bb0b4bf229.jpg", + "id": 19587, + "season": 2, + "likes": 0 + }, + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-501bb144e6a46.jpg", + "id": 19588, + "season": 0, + "likes": 0 + }, + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-51c953105ef77.jpg", + "id": 30309, + "season": 3, + "likes": 0 + } + ], + "banners": [ + { + "lang": "he", + "url": "http://assets.fanart.tv/fanart/tv/239761/tvbanner/wilfred-us-52403a7185070.jpg", + "likes": 1, + "id": 33755 + }, + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/tvbanner/wilfred-us-5265193db51f7.jpg", + "likes": 0, + "id": 34716 + } + ], + "hdlogos": [ + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-505f373be58e6.png", + "likes": 1, + "id": 21101 + }, + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-517ac360def17.png", + "likes": 1, + "id": 28248 + }, + { + "lang": "he", + "url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-52402df7ed945.png", + "likes": 1, + "id": 33750 + }, + { + "lang": "en", + "url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-51f556fb4abd3.png", + "likes": 0, + "id": 31816 + } + ], + "tvdbid": "239761" +} diff --git a/lib/fanart/tests/response/50x50.png b/lib/fanart/tests/response/50x50.png new file mode 100644 index 0000000000000000000000000000000000000000..0ba41614ca1b571daa258e917d1a1e6f5143790c Binary files /dev/null and b/lib/fanart/tests/response/50x50.png differ diff --git a/lib/fanart/tests/response/movie_thg.json b/lib/fanart/tests/response/movie_thg.json new file mode 100644 index 0000000000000000000000000000000000000000..77b6130e9983e5e07922e6ebbf0caed068824740 --- /dev/null +++ b/lib/fanart/tests/response/movie_thg.json @@ -0,0 +1,174 @@ +{ + "The Hunger Games": { + "tmdb_id": "70160", + "imdb_id": "tt1392170", + "movieart": [ + { + "id": "1226", + "url": "http://assets.fanart.tv/fanart/movies/70160/movieart/the-hunger-games-4f6dc995edb8f.png", + "lang": "en", + "likes": "3" + }, + { + "id": "1225", + "url": "http://assets.fanart.tv/fanart/movies/70160/movieart/the-hunger-games-4f6dc980b4514.png", + "lang": "en", + "likes": "1" + } + ], + "movielogo": [ + { + "id": "1230", + "url": "http://assets.fanart.tv/fanart/movies/70160/movielogo/the-hunger-games-4f6e0e63a9d29.png", + "lang": "en", + "likes": "2" + }, + { + "id": "8020", + "url": "http://assets.fanart.tv/fanart/movies/70160/movielogo/the-hunger-games-5018f873b5188.png", + "lang": "en", + "likes": "1" + }, + { + "id": "1224", + "url": "http://assets.fanart.tv/fanart/movies/70160/movielogo/the-hunger-games-4f6dc95a08de1.png", + "lang": "en", + "likes": "0" + } + ], + "moviedisc": [ + { + "id": "8431", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviedisc/the-hunger-games-501db4437623f.png", + "lang": "en", + "likes": "1", + "disc": "1", + "disc_type": "dvd" + }, + { + "id": "9787", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviedisc/the-hunger-games-502fd6d695a60.png", + "lang": "en", + "likes": "1", + "disc": "1", + "disc_type": "bluray" + } + ], + "moviethumb": [ + { + "id": "10687", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviethumb/the-hunger-games-503c88b32cf66.jpg", + "lang": "en", + "likes": "0" + } + ], + "hdmovielogo": [ + { + "id": "13004", + "url": "http://assets.fanart.tv/fanart/movies/70160/hdmovielogo/the-hunger-games-50500118613e3.png", + "lang": "en", + "likes": "0" + } + ], + "moviebackground": [ + { + "id": "14043", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5057c79ad3c56.jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "14044", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5057c79ad5526.jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "15911", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071de49311d1.jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "15914", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071df619b835.jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "15917", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e01fee856.jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "15918", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e0adcc57a.jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "15919", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e12006159.jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "15921", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e206aa2ac.jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "15922", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e2869d774.jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "15925", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e30069b72.jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "15927", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e3c4979b7.jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "15930", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e5b3f039b.jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "15931", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e6369e812.jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "15936", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e8749e73a.jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "15937", + "url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e9913bfeb.jpg", + "lang": "en", + "likes": "0" + } + ], + "hdmovieclearart": [ + { + "id": "14104", + "url": "http://assets.fanart.tv/fanart/movies/70160/hdmovieclearart/the-hunger-games-50582453b1375.png", + "lang": "en", + "likes": "0" + } + ] + } +} diff --git a/lib/fanart/tests/response/music_a7f.json b/lib/fanart/tests/response/music_a7f.json new file mode 100644 index 0000000000000000000000000000000000000000..de1a123fca3349dee52157e43d28ca057d458f99 --- /dev/null +++ b/lib/fanart/tests/response/music_a7f.json @@ -0,0 +1,171 @@ +{ + "Avenged Sevenfold": { + "mbid_id": "24e1b53c-3085-4581-8472-0b0088d2508c", + "artistbackground": [ + { + "id": "3027", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/artistbackground/avenged-sevenfold-4ddd7889a0fcf.jpg", + "likes": "0" + }, + { + "id": "64046", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/artistbackground/avenged-sevenfold-50c4db9a2c6e2.jpg", + "likes": "0" + }, + { + "id": "64048", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/artistbackground/avenged-sevenfold-50c4dc653f004.jpg", + "likes": "0" + } + ], + "albums": { + "180560ee-2d9d-33cf-8de7-cdaaba610739": { + "albumcover": [ + { + "id": "3028", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/albumcover/city-of-evil-4ddd79ca0beea.jpg", + "likes": "0" + } + ], + "cdart": [ + { + "id": "9921", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/cdart/city-of-evil-4e5f7b9f50d37.png", + "likes": "0", + "disc": "1", + "size": "1000" + } + ] + }, + "1c7120ae-32b6-3693-8974-599977b01601": { + "albumcover": [ + { + "id": "3029", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/albumcover/waking-the-fallen-4ddd79ca1b11e.jpg", + "likes": "0" + } + ], + "cdart": [ + { + "id": "9922", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/cdart/waking-the-fallen-4e5f7b9f5ebdf.png", + "likes": "0", + "disc": "1", + "size": "1000" + } + ] + }, + "94672194-7f42-3965-a489-f2f3cdc1c79e": { + "albumcover": [ + { + "id": "3030", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/albumcover/avenged-sevenfold-4ddd79ca1bcd6.jpg", + "likes": "0" + } + ], + "cdart": [ + { + "id": "9923", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/cdart/avenged-sevenfold-4e5f7b9f5fb7f.png", + "likes": "0", + "disc": "1", + "size": "1000" + } + ] + }, + "9d642393-0005-3e89-b3d4-35d89c2f6ad6": { + "albumcover": [ + { + "id": "3031", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/albumcover/sounding-the-seventh-trumpet-4ddd79ca1d05e.jpg", + "likes": "0" + } + ], + "cdart": [ + { + "id": "9924", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/cdart/sounding-the-seventh-trumpet-4e5f7b9f62e47.png", + "likes": "0", + "disc": "1", + "size": "1000" + } + ] + }, + "fe4373ed-5e89-46b3-b4c0-31433ce217df": { + "albumcover": [ + { + "id": "3032", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/albumcover/nightmare-4ddd79ca1dffe.jpg", + "likes": "0" + } + ], + "cdart": [ + { + "id": "11630", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/cdart/nightmare-4e8059a3c581c.png", + "likes": "0", + "disc": "1", + "size": "1000" + } + ] + }, + "41d1b72b-1eee-3319-937f-c85d6d2fcfbb": { + "albumcover": [ + { + "id": "61014", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/albumcover/warmness-on-the-soul-509d2e9150bf4.jpg", + "likes": "0" + } + ] + } + }, + "musiclogo": [ + { + "id": "5712", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/musiclogo/avenged-sevenfold-4dfc8aee78b49.png", + "likes": "0" + }, + { + "id": "41835", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/musiclogo/avenged-sevenfold-4ffc75f3a7e54.png", + "likes": "0" + }, + { + "id": "41836", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/musiclogo/avenged-sevenfold-4ffc75f3a8473.png", + "likes": "0" + } + ], + "artistthumb": [ + { + "id": "31109", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/artistthumb/avenged-sevenfold-4fb2b533bc73a.jpg", + "likes": "0" + }, + { + "id": "64042", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/artistthumb/avenged-sevenfold-50c4d9279d6e9.jpg", + "likes": "0" + } + ], + "hdmusiclogo": [ + { + "id": "49644", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/hdmusiclogo/avenged-sevenfold-503fcebece042.png", + "likes": "0" + }, + { + "id": "49645", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/hdmusiclogo/avenged-sevenfold-503fcebecf17e.png", + "likes": "0" + } + ], + "musicbanner": [ + { + "id": "52630", + "url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/musicbanner/avenged-sevenfold-505b2346a559d.jpg", + "likes": "0" + } + ] + } +} diff --git a/lib/fanart/tests/response/tv_239761.json b/lib/fanart/tests/response/tv_239761.json new file mode 100644 index 0000000000000000000000000000000000000000..bce4fda2574eeaf78f39b89c3fdaca575a4c7558 --- /dev/null +++ b/lib/fanart/tests/response/tv_239761.json @@ -0,0 +1,196 @@ +{ + "Wilfred (US)": { + "hdclearart": [ + { + "url": "http://assets.fanart.tv/fanart/tv/239761/hdclearart/wilfred-us-505f94ed0ba13.png", + "lang": "en", + "id": "21112", + "likes": "1" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/hdclearart/wilfred-us-52403264aa3ec.png", + "lang": "he", + "id": "33751", + "likes": "1" + } + ], + "seasonthumb": [ + { + "url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-52403782bab55.jpg", + "lang": "he", + "id": "33752", + "season": "1", + "likes": "1" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-5240379335232.jpg", + "lang": "he", + "id": "33753", + "season": "2", + "likes": "1" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-524037bc83c7d.jpg", + "lang": "he", + "id": "33754", + "season": "3", + "likes": "1" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-501bb0a8e60f9.jpg", + "lang": "en", + "id": "19586", + "season": "1", + "likes": "0" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-501bb0b4bf229.jpg", + "lang": "en", + "id": "19587", + "season": "2", + "likes": "0" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-501bb144e6a46.jpg", + "lang": "en", + "id": "19588", + "season": "0", + "likes": "0" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-51c953105ef77.jpg", + "lang": "en", + "id": "30309", + "season": "3", + "likes": "0" + } + ], + "tvbanner": [ + { + "url": "http://assets.fanart.tv/fanart/tv/239761/tvbanner/wilfred-us-52403a7185070.jpg", + "lang": "he", + "id": "33755", + "likes": "1" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/tvbanner/wilfred-us-5265193db51f7.jpg", + "lang": "en", + "id": "34716", + "likes": "0" + } + ], + "thetvdb_id": "239761", + "clearlogo": [ + { + "url": "http://assets.fanart.tv/fanart/tv/239761/clearlogo/wilfred-us-4e04b6495dfd3.png", + "lang": "en", + "id": "11977", + "likes": "2" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/clearlogo/wilfred-us-517ac36e39f67.png", + "lang": "en", + "id": "28249", + "likes": "1" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/clearlogo/wilfred-us-51f557082cfde.png", + "lang": "en", + "id": "31817", + "likes": "0" + } + ], + "tvposter": [ + { + "url": "http://assets.fanart.tv/fanart/tv/239761/tvposter/wilfred-us-525d893230d7c.jpg", + "lang": "he", + "id": "34584", + "likes": "1" + } + ], + "showbackground": [ + { + "url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-5034dbd49115e.jpg", + "lang": "en", + "id": "19965", + "season": "all", + "likes": "0" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-50b0c92db6973.jpg", + "lang": "en", + "id": "23166", + "season": "all", + "likes": "0" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-50b0c92dbb46b.jpg", + "lang": "en", + "id": "23167", + "season": "all", + "likes": "0" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-50b0c92dbb9d1.jpg", + "lang": "en", + "id": "23168", + "season": "all", + "likes": "0" + } + ], + "tvthumb": [ + { + "url": "http://assets.fanart.tv/fanart/tv/239761/tvthumb/wilfred-us-501cf526174fe.jpg", + "lang": "en", + "id": "19596", + "likes": "1" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/tvthumb/wilfred-us-51bfb4a105904.jpg", + "lang": "en", + "id": "30060", + "likes": "0" + } + ], + "clearart": [ + { + "url": "http://assets.fanart.tv/fanart/tv/239761/clearart/wilfred-us-4e05f10e87711.png", + "lang": "en", + "id": "11987", + "likes": "2" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/clearart/wilfred-us-4e2f151d5ed62.png", + "lang": "en", + "id": "12470", + "likes": "1" + } + ], + "hdtvlogo": [ + { + "url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-505f373be58e6.png", + "lang": "en", + "id": "21101", + "likes": "1" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-517ac360def17.png", + "lang": "en", + "id": "28248", + "likes": "1" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-52402df7ed945.png", + "lang": "he", + "id": "33750", + "likes": "1" + }, + { + "url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-51f556fb4abd3.png", + "lang": "en", + "id": "31816", + "likes": "0" + } + ] + } +} diff --git a/lib/fanart/tests/response/tv_79349.json b/lib/fanart/tests/response/tv_79349.json new file mode 100644 index 0000000000000000000000000000000000000000..73d1698b9123f30bd5eed4b98d7f13445ba1ccdc --- /dev/null +++ b/lib/fanart/tests/response/tv_79349.json @@ -0,0 +1,756 @@ +{ + "Dexter": { + "thetvdb_id": "79349", + "hdtvlogo": [ + { + "id": "20959", + "url": "http://assets.fanart.tv/fanart/tv/79349/hdtvlogo/dexter-50575994eb118.png", + "lang": "en", + "likes": "10" + }, + { + "id": "20378", + "url": "http://assets.fanart.tv/fanart/tv/79349/hdtvlogo/dexter-503fc2f24d9b3.png", + "lang": "en", + "likes": "5" + } + ], + "hdclearart": [ + { + "id": "23059", + "url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-50af98e73b0a5.png", + "lang": "en", + "likes": "8" + }, + { + "id": "24313", + "url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-50eb4363da522.png", + "lang": "en", + "likes": "5" + }, + { + "id": "20560", + "url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-504775fd50557.png", + "lang": "en", + "likes": "4" + }, + { + "id": "29495", + "url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-51aa63100548b.png", + "lang": "en", + "likes": "3" + }, + { + "id": "26712", + "url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-51400b1672938.png", + "lang": "en", + "likes": "1" + }, + { + "id": "29496", + "url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-51aa724f0a2ab.png", + "lang": "en", + "likes": "1" + }, + { + "id": "29505", + "url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-51aab23851368.png", + "lang": "en", + "likes": "1" + }, + { + "id": "29594", + "url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-51afbcdf38d5e.png", + "lang": "en", + "likes": "1" + }, + { + "id": "29595", + "url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-51afbcdf3ea8e.png", + "lang": "en", + "likes": "1" + } + ], + "clearlogo": [ + { + "id": "20958", + "url": "http://assets.fanart.tv/fanart/tv/79349/clearlogo/dexter-5057573260826.png", + "lang": "en", + "likes": "6" + }, + { + "id": "2114", + "url": "http://assets.fanart.tv/fanart/tv/79349/clearlogo/Dexter-79349-2.png", + "lang": "en", + "likes": "4" + }, + { + "id": "14577", + "url": "http://assets.fanart.tv/fanart/tv/79349/clearlogo/dexter-4ecdf0c030189.png", + "lang": "en", + "likes": "3" + }, + { + "id": "16685", + "url": "http://assets.fanart.tv/fanart/tv/79349/clearlogo/dexter-4f6879db58edf.png", + "lang": "ru", + "likes": "1" + } + ], + "characterart": [ + { + "id": "16825", + "url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-4f76318ae4410.png", + "lang": "en", + "likes": "5" + }, + { + "id": "29497", + "url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-51aa726346bcf.png", + "lang": "en", + "likes": "3" + }, + { + "id": "14981", + "url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-4eface5cee809.png", + "lang": "en", + "likes": "1" + }, + { + "id": "16996", + "url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-4f8189d220d4b.png", + "lang": "en", + "likes": "1" + }, + { + "id": "26713", + "url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-51400b26c65de.png", + "lang": "en", + "likes": "1" + }, + { + "id": "29597", + "url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-51afbcf6002a7.png", + "lang": "en", + "likes": "1" + }, + { + "id": "29598", + "url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-51afbcf6006e6.png", + "lang": "en", + "likes": "1" + }, + { + "id": "29646", + "url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-51b0fc45e0dc0.png", + "lang": "en", + "likes": "1" + } + ], + "clearart": [ + { + "id": "4980", + "url": "http://assets.fanart.tv/fanart/tv/79349/clearart/D_79349 (3).png", + "lang": "en", + "likes": "4" + }, + { + "id": "14579", + "url": "http://assets.fanart.tv/fanart/tv/79349/clearart/dexter-4ecdf0db2adf1.png", + "lang": "en", + "likes": "3" + }, + { + "id": "16682", + "url": "http://assets.fanart.tv/fanart/tv/79349/clearart/dexter-4f68753540f2d.png", + "lang": "ru", + "likes": "1" + }, + { + "id": "4982", + "url": "http://assets.fanart.tv/fanart/tv/79349/clearart/D_79349.png", + "lang": "en", + "likes": "0" + }, + { + "id": "4983", + "url": "http://assets.fanart.tv/fanart/tv/79349/clearart/D_79349 (1).png", + "lang": "en", + "likes": "0" + }, + { + "id": "4984", + "url": "http://assets.fanart.tv/fanart/tv/79349/clearart/D_79349 (0).png", + "lang": "en", + "likes": "0" + }, + { + "id": "14578", + "url": "http://assets.fanart.tv/fanart/tv/79349/clearart/dexter-4ecdf0cf3fb38.png", + "lang": "en", + "likes": "0" + }, + { + "id": "17196", + "url": "http://assets.fanart.tv/fanart/tv/79349/clearart/dexter-4f8af83f3bde7.png", + "lang": "en", + "likes": "0" + } + ], + "showbackground": [ + { + "id": "18467", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fc683691dea7.jpg", + "lang": "en", + "likes": "4", + "season": "1" + }, + { + "id": "18950", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fdf608e2df53.jpg", + "lang": "en", + "likes": "2", + "season": "3" + }, + { + "id": "18466", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fc6830dc2ccc.jpg", + "lang": "en", + "likes": "1", + "season": "4" + }, + { + "id": "18468", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fc683a5ab451.jpg", + "lang": "en", + "likes": "1", + "season": "6" + }, + { + "id": "21524", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-506bdd9c35771.jpg", + "lang": "en", + "likes": "1", + "season": "all" + }, + { + "id": "21526", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-506bddc9f04cb.jpg", + "lang": "en", + "likes": "1", + "season": "" + }, + { + "id": "21530", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-506bde2654668.jpg", + "lang": "en", + "likes": "1", + "season": "all" + }, + { + "id": "24058", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-50def777ea9c8.jpg", + "lang": "en", + "likes": "1", + "season": "all" + }, + { + "id": "18515", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fc8eab16803c.jpg", + "lang": "en", + "likes": "0", + "season": "all" + }, + { + "id": "18947", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fdf5e107be0d.jpg", + "lang": "en", + "likes": "0", + "season": "5" + }, + { + "id": "18949", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fdf601385517.jpg", + "lang": "en", + "likes": "0", + "season": "all" + }, + { + "id": "18952", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fdf6386ce1c1.jpg", + "lang": "en", + "likes": "0", + "season": "all" + }, + { + "id": "21525", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-506bddb3bd3f4.jpg", + "lang": "en", + "likes": "0", + "season": "all" + }, + { + "id": "21527", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-506bdddc3f476.jpg", + "lang": "en", + "likes": "0", + "season": "all" + }, + { + "id": "21529", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-506bde113406e.jpg", + "lang": "en", + "likes": "0", + "season": "all" + }, + { + "id": "24046", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-50de1f84e736f.jpg", + "lang": "en", + "likes": "0", + "season": "all" + }, + { + "id": "24048", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-50de1f84e7d57.jpg", + "lang": "en", + "likes": "0", + "season": "all" + }, + { + "id": "24049", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-50de21ac3ae25.jpg", + "lang": "en", + "likes": "0", + "season": "all" + }, + { + "id": "24054", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-50def777e84d0.jpg", + "lang": "en", + "likes": "0", + "season": "all" + }, + { + "id": "24055", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-50def777e8dbc.jpg", + "lang": "en", + "likes": "0", + "season": "all" + }, + { + "id": "24056", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-50def777e9762.jpg", + "lang": "en", + "likes": "0", + "season": "all" + }, + { + "id": "24986", + "url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-5101fa187c857.jpg", + "lang": "en", + "likes": "0", + "season": "all" + } + ], + "seasonthumb": [ + { + "id": "18986", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fe21b7708ebe.jpg", + "lang": "en", + "likes": "3", + "season": "6" + }, + { + "id": "5002", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (6).jpg", + "lang": "en", + "likes": "1", + "season": "3" + }, + { + "id": "5003", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (5).jpg", + "lang": "en", + "likes": "1", + "season": "1" + }, + { + "id": "17802", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fa981a7251d7.jpg", + "lang": "en", + "likes": "1", + "season": "5" + }, + { + "id": "17823", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4faab0bccbfb6.jpg", + "lang": "en", + "likes": "1", + "season": "6" + }, + { + "id": "18980", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fe21a6955116.jpg", + "lang": "en", + "likes": "1", + "season": "1" + }, + { + "id": "18982", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fe21b0767edb.jpg", + "lang": "en", + "likes": "1", + "season": "2" + }, + { + "id": "18983", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fe21b292d661.jpg", + "lang": "en", + "likes": "1", + "season": "3" + }, + { + "id": "18984", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fe21b42d983d.jpg", + "lang": "en", + "likes": "1", + "season": "4" + }, + { + "id": "18985", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fe21b5847d7b.jpg", + "lang": "en", + "likes": "1", + "season": "5" + }, + { + "id": "21883", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-5071800d37e80.jpg", + "lang": "en", + "likes": "1", + "season": "7" + }, + { + "id": "4989", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (9).jpg", + "lang": "en", + "likes": "0", + "season": "all" + }, + { + "id": "4990", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (19).jpg", + "lang": "en", + "likes": "0", + "season": "4" + }, + { + "id": "4991", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (18).jpg", + "lang": "en", + "likes": "0", + "season": "4" + }, + { + "id": "4992", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (17).jpg", + "lang": "en", + "likes": "0", + "season": "3" + }, + { + "id": "4993", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (16).jpg", + "lang": "en", + "likes": "0", + "season": "2" + }, + { + "id": "4994", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (15).jpg", + "lang": "en", + "likes": "0", + "season": "1" + }, + { + "id": "4995", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (14).jpg", + "lang": "en", + "likes": "0", + "season": "all" + }, + { + "id": "4996", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (13).jpg", + "lang": "en", + "likes": "0", + "season": "4" + }, + { + "id": "4997", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (12).jpg", + "lang": "en", + "likes": "0", + "season": "3" + }, + { + "id": "4998", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (11).jpg", + "lang": "en", + "likes": "0", + "season": "2" + }, + { + "id": "4999", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (10).jpg", + "lang": "en", + "likes": "0", + "season": "1" + }, + { + "id": "5000", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (8).jpg", + "lang": "en", + "likes": "0", + "season": "2" + }, + { + "id": "5001", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (7).jpg", + "lang": "en", + "likes": "0", + "season": "4" + }, + { + "id": "5004", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter.jpg", + "lang": "en", + "likes": "0", + "season": "all" + }, + { + "id": "5005", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (4).jpg", + "lang": "en", + "likes": "0", + "season": "5" + }, + { + "id": "5006", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (3).jpg", + "lang": "en", + "likes": "0", + "season": "4" + }, + { + "id": "5007", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (2).jpg", + "lang": "en", + "likes": "0", + "season": "3" + }, + { + "id": "5008", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (1).jpg", + "lang": "en", + "likes": "0", + "season": "2" + }, + { + "id": "5009", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (0).jpg", + "lang": "en", + "likes": "0", + "season": "1" + }, + { + "id": "17803", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fa981a7258fb.jpg", + "lang": "en", + "likes": "0", + "season": "5" + }, + { + "id": "17804", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fa981a725c14.jpg", + "lang": "en", + "likes": "0", + "season": "5" + }, + { + "id": "17805", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fa981c6607e4.jpg", + "lang": "en", + "likes": "0", + "season": "0" + }, + { + "id": "17807", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fa98ac2b811d.jpg", + "lang": "en", + "likes": "0", + "season": "6" + }, + { + "id": "17808", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fa98ac2b87ab.jpg", + "lang": "en", + "likes": "0", + "season": "6" + }, + { + "id": "17810", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fa994697afa3.jpg", + "lang": "en", + "likes": "0", + "season": "6" + }, + { + "id": "18514", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fc8e9fa79bf8.jpg", + "lang": "en", + "likes": "0", + "season": "7" + }, + { + "id": "31022", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-51dc720661cb7.jpg", + "lang": "en", + "likes": "0", + "season": "8" + }, + { + "id": "31023", + "url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-51dc72a19a0bb.jpg", + "lang": "en", + "likes": "0", + "season": "8" + } + ], + "tvthumb": [ + { + "id": "5012", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (10).jpg", + "lang": "en", + "likes": "2" + }, + { + "id": "5023", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (0).jpg", + "lang": "en", + "likes": "2" + }, + { + "id": "14580", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/dexter-4ecdf5027a53c.jpg", + "lang": "en", + "likes": "2" + }, + { + "id": "5013", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (9).jpg", + "lang": "en", + "likes": "1" + }, + { + "id": "5016", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (6).jpg", + "lang": "en", + "likes": "1" + }, + { + "id": "5020", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (2).jpg", + "lang": "en", + "likes": "1" + }, + { + "id": "29341", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/dexter-51a338d376b4a.jpg", + "lang": "de", + "likes": "1" + }, + { + "id": "31722", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/dexter-51f27112a2a89.jpg", + "lang": "en", + "likes": "1" + }, + { + "id": "5010", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (12).jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "5011", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (11).jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "5014", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (8).jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "5015", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (7).jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "5017", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (5).jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "5018", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (4).jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "5019", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (3).jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "5021", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349.jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "5022", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (1).jpg", + "lang": "en", + "likes": "0" + }, + { + "id": "14277", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/dexter-4ead4375923fd.jpg", + "lang": "en", + "likes": "0" + } + ], + "tvbanner": [ + { + "id": "30062", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvbanner/dexter-51bfc857c84fd.jpg", + "lang": "en", + "likes": "1" + }, + { + "id": "30063", + "url": "http://assets.fanart.tv/fanart/tv/79349/tvbanner/dexter-51bfc89667267.jpg", + "lang": "en", + "likes": "1" + } + ] + } +} \ No newline at end of file diff --git a/lib/fanart/tests/test_core.py b/lib/fanart/tests/test_core.py new file mode 100644 index 0000000000000000000000000000000000000000..2cdb71fbceaea55e9d1828927e26a607c8cfad04 --- /dev/null +++ b/lib/fanart/tests/test_core.py @@ -0,0 +1,23 @@ +from unittest import TestCase +from fanart.core import Request +from fanart.errors import RequestFanartError, ResponseFanartError +from httpretty import httprettified, HTTPretty + + +class RequestTestCase(TestCase): + def test_valitate_error(self): + self.assertRaises(RequestFanartError, Request, 'key', 'id', 'sport') + + @httprettified + def test_response_error(self): + request = Request('apikey', 'objid', 'series') + HTTPretty.register_uri( + HTTPretty.GET, + 'http://api.fanart.tv/webservice/series/apikey/objid/JSON/all/1/2', + body='Please specify a valid API key', + ) + try: + request.response() + except ResponseFanartError as e: + self.assertEqual(repr(e), "ResponseFanartError('No JSON object could be decoded',)") + self.assertEqual(str(e), 'No JSON object could be decoded') diff --git a/lib/fanart/tests/test_immutable.py b/lib/fanart/tests/test_immutable.py new file mode 100644 index 0000000000000000000000000000000000000000..8a0149dbe94c65f899d2e67400a5f130958a9b77 --- /dev/null +++ b/lib/fanart/tests/test_immutable.py @@ -0,0 +1,49 @@ +from unittest import TestCase +from fanart.immutable import Immutable + + +class TestMutable(object): + def __init__(self, spam, ham, eggs): + self.spam = spam + self.ham = ham + self.eggs = eggs + + @Immutable.mutablemethod + def anyway(self): + self.spam = self.ham + self.eggs + + +class TestImmutable(TestMutable, Immutable): + @Immutable.mutablemethod + def __init__(self, *args, **kwargs): + super(TestImmutable, self).__init__(*args, **kwargs) + + +class ImmutableTestCase(TestCase): + def setUp(self): + self.instance = TestImmutable('spam', 'ham', 'eggs') + + def test_set_raises(self): + self.assertRaises(TypeError, self.instance.__setattr__, 'spam', 'ham') + + def test_set(self): + self.instance._mutable = True + self.instance.spam = 'ham' + self.assertEqual(self.instance.spam, 'ham') + + def test_del_raises(self): + self.assertRaises(TypeError, self.instance.__delattr__, 'spam') + + def test_del(self): + self.instance._mutable = True + del self.instance.spam + self.assertRaises(AttributeError, self.instance.__getattribute__, 'spam') + + def test_equal(self): + new_instance = TestImmutable('spam', 'ham', 'eggs') + self.assertEqual(self.instance, new_instance) + + def test_mutable_dec(self): + instance = TestMutable('spam', 'ham', 'eggs') + instance.anyway() + self.assertEqual(instance.spam, 'hameggs') diff --git a/lib/fanart/tests/test_items.py b/lib/fanart/tests/test_items.py new file mode 100644 index 0000000000000000000000000000000000000000..e6c304389bba2d00d7b52ba2071e64bff0859f0a --- /dev/null +++ b/lib/fanart/tests/test_items.py @@ -0,0 +1,27 @@ +from unittest import TestCase +import os +from fanart.items import LeafItem +from httpretty import httprettified, HTTPretty +from fanart.tests import LOCALDIR + + +class LeafItemTestCase(TestCase): + def setUp(self): + self.leaf = LeafItem(id=11977, likes=2, url='http://test.tv/50x50.txt') + + def test_str(self): + self.assertEqual(str(self.leaf), 'http://test.tv/50x50.txt') + + @httprettified + def test_content(self): + with open(os.path.join(LOCALDIR, 'response/50x50.png')) as fp: + body = fp.read() + HTTPretty.register_uri( + HTTPretty.GET, + 'http://test.tv/50x50.txt', + body=body + ) + self.assertEqual(self.leaf.content(), body) + self.assertEqual(len(HTTPretty.latest_requests), 1) + self.assertEqual(self.leaf.content(), body) # Cached + self.assertEqual(len(HTTPretty.latest_requests), 1) diff --git a/lib/fanart/tests/test_movie.py b/lib/fanart/tests/test_movie.py new file mode 100644 index 0000000000000000000000000000000000000000..f127c28e2a0c417781cac4d3159aa3f1a1be2e0b --- /dev/null +++ b/lib/fanart/tests/test_movie.py @@ -0,0 +1,21 @@ +import os +import unittest +from httpretty import HTTPretty, httprettified +from fanart.movie import * +from fanart.tests import LOCALDIR +os.environ['FANART_APIKEY'] = 'e3c7f0d0beeaf45b3a0dd3b9dd8a3338' + + +class TvItemTestCase(unittest.TestCase): + @httprettified + def test_get(self): + with open(os.path.join(LOCALDIR, 'response/movie_thg.json')) as fp: + body = fp.read() + HTTPretty.register_uri( + HTTPretty.GET, + 'http://api.fanart.tv/webservice/movie/e3c7f0d0beeaf45b3a0dd3b9dd8a3338/70160/JSON/all/1/2', + body=body + ) + hunger_games = Movie.get(id=70160) + self.assertEqual(hunger_games.tmdbid, '70160') + self.assertEqual(hunger_games, eval(repr(hunger_games))) diff --git a/lib/fanart/tests/test_music.py b/lib/fanart/tests/test_music.py new file mode 100644 index 0000000000000000000000000000000000000000..8c5d107d8406d93c288ec5236aeb4a8a9460eca9 --- /dev/null +++ b/lib/fanart/tests/test_music.py @@ -0,0 +1,22 @@ +import os +import unittest +from httpretty import HTTPretty, httprettified +from fanart.music import * +from fanart.tests import LOCALDIR +os.environ['FANART_APIKEY'] = 'e3c7f0d0beeaf45b3a0dd3b9dd8a3338' + + +class ArtistItemTestCase(unittest.TestCase): + @httprettified + def test_get(self): + with open(os.path.join(LOCALDIR, 'response/music_a7f.json')) as fp: + body = fp.read() + HTTPretty.register_uri( + HTTPretty.GET, + 'http://api.fanart.tv/webservice/artist/e3c7f0d0beeaf45b3a0dd3b9dd8a3338/24e1b53c-3085-4581-8472-0b0088d2508c/JSON/all/1/2', + body=body + ) + a7f = Artist.get(id='24e1b53c-3085-4581-8472-0b0088d2508c') + self.assertEqual(a7f.mbid, '24e1b53c-3085-4581-8472-0b0088d2508c') + self.assertEqual(a7f, eval(repr(a7f))) + self.assertEqual(len(a7f.thumbs), 2) diff --git a/lib/fanart/tests/test_tv.py b/lib/fanart/tests/test_tv.py new file mode 100644 index 0000000000000000000000000000000000000000..eb5e742576028be765eee84fa132bb4bb9569002 --- /dev/null +++ b/lib/fanart/tests/test_tv.py @@ -0,0 +1,46 @@ +import json +from fanart.errors import ResponseFanartError +import os +import unittest +from httpretty import HTTPretty, httprettified +from fanart.tv import * +from fanart.tests import LOCALDIR +os.environ['FANART_APIKEY'] = 'e3c7f0d0beeaf45b3a0dd3b9dd8a3338' + + +class TvItemTestCase(unittest.TestCase): + @httprettified + def test_get_wilfred(self): + with open(os.path.join(LOCALDIR, 'response/tv_239761.json')) as fp: + body = fp.read() + HTTPretty.register_uri( + HTTPretty.GET, + 'http://api.fanart.tv/webservice/series/e3c7f0d0beeaf45b3a0dd3b9dd8a3338/239761/JSON/all/1/2', + body=body + ) + wilfred = TvShow.get(id=239761) + self.assertEqual(wilfred.tvdbid, '239761') + with open(os.path.join(LOCALDIR, 'json/wilfred.json')) as fp: + self.assertEqual(json.loads(wilfred.json()), json.load(fp)) + + @httprettified + def test_get_dexter(self): + with open(os.path.join(LOCALDIR, 'response/tv_79349.json')) as fp: + body = fp.read() + HTTPretty.register_uri( + HTTPretty.GET, + 'http://api.fanart.tv/webservice/series/e3c7f0d0beeaf45b3a0dd3b9dd8a3338/79349/JSON/all/1/2', + body=body + ) + dexter = TvShow.get(id=79349) + self.assertEqual(dexter.tvdbid, '79349') + self.assertEqual(dexter, eval(repr(dexter))) + + @httprettified + def test_get_null(self): + HTTPretty.register_uri( + HTTPretty.GET, + 'http://api.fanart.tv/webservice/series/e3c7f0d0beeaf45b3a0dd3b9dd8a3338/79349/JSON/all/1/2', + body='null' + ) + self.assertRaises(ResponseFanartError, TvShow.get, id=79349) diff --git a/lib/fanart/tv.py b/lib/fanart/tv.py new file mode 100644 index 0000000000000000000000000000000000000000..beedff621a983ff1971364cadddb966a03cc0813 --- /dev/null +++ b/lib/fanart/tv.py @@ -0,0 +1,108 @@ +import fanart +from fanart.items import LeafItem, Immutable, ResourceItem +__all__ = ( + 'CharacterItem', + 'ArtItem', + 'LogoItem', + 'BackgroundItem', + 'SeasonItem', + 'ThumbItem', + 'HdLogoItem', + 'HdArtItem', + 'PosterItem', + 'BannerItem', + 'TvShow', +) + + +class TvItem(LeafItem): + @Immutable.mutablemethod + def __init__(self, id, url, likes, lang): + super(TvItem, self).__init__(id, url, likes) + self.lang = lang + + +class SeasonedTvItem(TvItem): + @Immutable.mutablemethod + def __init__(self, id, url, likes, lang, season): + super(SeasonedTvItem, self).__init__(id, url, likes, lang) + self.season = 0 if season == 'all' else int(season or 0) + + +class CharacterItem(TvItem): + KEY = fanart.TYPE.TV.CHARACTER + + +class ArtItem(TvItem): + KEY = fanart.TYPE.TV.ART + + +class LogoItem(TvItem): + KEY = fanart.TYPE.TV.LOGO + + +class BackgroundItem(SeasonedTvItem): + KEY = fanart.TYPE.TV.BACKGROUND + + +class SeasonItem(SeasonedTvItem): + KEY = fanart.TYPE.TV.SEASONTHUMB + + +class ThumbItem(TvItem): + KEY = fanart.TYPE.TV.THUMB + + +class HdLogoItem(TvItem): + KEY = fanart.TYPE.TV.HDLOGO + + +class HdArtItem(TvItem): + KEY = fanart.TYPE.TV.HDART + + +class PosterItem(TvItem): + KEY = fanart.TYPE.TV.POSTER + + +class BannerItem(TvItem): + KEY = fanart.TYPE.TV.BANNER + + +class TvShow(ResourceItem): + WS = fanart.WS.TV + + @Immutable.mutablemethod + def __init__(self, name, tvdbid, backgrounds, characters, arts, logos, seasons, thumbs, hdlogos, hdarts, posters, + banners): + self.name = name + self.tvdbid = tvdbid + self.backgrounds = backgrounds + self.characters = characters + self.arts = arts + self.logos = logos + self.seasons = seasons + self.thumbs = thumbs + self.hdlogos = hdlogos + self.hdarts = hdarts + self.posters = posters + self.banners = banners + + @classmethod + def from_dict(cls, resource): + assert len(resource) == 1, 'Bad Format Map' + name, resource = resource.items()[0] + return cls( + name=name, + tvdbid=resource['thetvdb_id'], + backgrounds=BackgroundItem.extract(resource), + characters=CharacterItem.extract(resource), + arts=ArtItem.extract(resource), + logos=LogoItem.extract(resource), + seasons=SeasonItem.extract(resource), + thumbs=ThumbItem.extract(resource), + hdlogos=HdLogoItem.extract(resource), + hdarts=HdArtItem.extract(resource), + posters=PosterItem.extract(resource), + banners=BannerItem.extract(resource), + ) diff --git a/lib/tvdb_api/tvdb_api.py b/lib/tvdb_api/tvdb_api.py index c7843c734a2857f1c59473412ce9cddc5085ed7e..7c7ee02d901a7d052691d40040d9718d331d9a03 100644 --- a/lib/tvdb_api/tvdb_api.py +++ b/lib/tvdb_api/tvdb_api.py @@ -736,7 +736,7 @@ class Tvdb: return banners = {} - for cur_banner in bannersEt['banner']: + for cur_banner in bannersEt['banner'] if isinstance(bannersEt['banner'], list) else [bannersEt['banner']]: bid = cur_banner['id'] btype = cur_banner['bannertype'] btype2 = cur_banner['bannertype2'] @@ -797,7 +797,7 @@ class Tvdb: return cur_actors = Actors() - for cur_actor in actorsEt['actor']: + for cur_actor in actorsEt['actor'] if isinstance(actorsEt['actor'], list) else [actorsEt['actor']]: curActor = Actor() for k, v in cur_actor.items(): if k is None or v is None: diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 9f842a38adef659be9e7c6904e1a7eacee985892..a97434caedfe49aa17978c86ac919611bfd95a9a 100755 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -58,7 +58,7 @@ CFG = None CONFIG_FILE = None # This is the version of the config we EXPECT to find -CONFIG_VERSION = 5 +CONFIG_VERSION = 6 # Default encryption version (0 for None) ENCRYPTION_VERSION = 0 @@ -148,7 +148,6 @@ CPU_PRESET = None ANON_REDIRECT = None -USE_API = False API_KEY = None API_ROOT = None @@ -472,6 +471,7 @@ CALENDAR_UNPROTECTED = False TMDB_API_KEY = 'edc5f123313769de83a71e157758030b' TRAKT_API_KEY = 'abd806c54516240c76e4ebc9c5ccf394' +FANART_API_KEY = '9b3afaf26f6241bdb57d6cc6bd798da7' __INITIALIZED__ = False @@ -484,7 +484,7 @@ def get_backlog_cycle_time(): def initialize(consoleLogging=True): with INIT_LOCK: - global BRANCH, GIT_RESET, GIT_REMOTE, GIT_REMOTE_URL, CUR_COMMIT_HASH, CUR_COMMIT_BRANCH, ACTUAL_LOG_DIR, LOG_DIR, WEB_PORT, WEB_LOG, ENCRYPTION_VERSION, WEB_ROOT, WEB_USERNAME, WEB_PASSWORD, WEB_HOST, WEB_IPV6, USE_API, API_KEY, API_ROOT, ENABLE_HTTPS, HTTPS_CERT, HTTPS_KEY, \ + global BRANCH, GIT_RESET, GIT_REMOTE, GIT_REMOTE_URL, CUR_COMMIT_HASH, CUR_COMMIT_BRANCH, ACTUAL_LOG_DIR, LOG_DIR, WEB_PORT, WEB_LOG, ENCRYPTION_VERSION, WEB_ROOT, WEB_USERNAME, WEB_PASSWORD, WEB_HOST, WEB_IPV6, API_KEY, API_ROOT, ENABLE_HTTPS, HTTPS_CERT, HTTPS_KEY, \ HANDLE_REVERSE_PROXY, USE_NZBS, USE_TORRENTS, NZB_METHOD, NZB_DIR, DOWNLOAD_PROPERS, RANDOMIZE_PROVIDERS, CHECK_PROPERS_INTERVAL, ALLOW_HIGH_PRIORITY, TORRENT_METHOD, \ SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, SAB_CATEGORY_ANIME, SAB_HOST, \ NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_CATEGORY_ANIME, NZBGET_PRIORITY, NZBGET_HOST, NZBGET_USE_HTTPS, backlogSearchScheduler, \ @@ -658,7 +658,6 @@ def initialize(consoleLogging=True): SORT_ARTICLE = bool(check_setting_int(CFG, 'General', 'sort_article', 0)) - USE_API = bool(check_setting_int(CFG, 'General', 'use_api', 0)) API_KEY = check_setting_str(CFG, 'General', 'api_key', '', censor_log=True) ENABLE_HTTPS = bool(check_setting_int(CFG, 'General', 'enable_https', 0)) @@ -1435,7 +1434,6 @@ def save_config(): new_config['General']['localhost_ip'] = LOCALHOST_IP new_config['General']['cpu_preset'] = CPU_PRESET new_config['General']['anon_redirect'] = ANON_REDIRECT - new_config['General']['use_api'] = int(USE_API) new_config['General']['api_key'] = API_KEY new_config['General']['debug'] = int(DEBUG) new_config['General']['enable_https'] = int(ENABLE_HTTPS) diff --git a/sickbeard/classes.py b/sickbeard/classes.py index 7a0420a5d11a883ddf7ad46090d67c8927d43bf1..d6f643d355d8653c835821bcc31a27198affc80e 100644 --- a/sickbeard/classes.py +++ b/sickbeard/classes.py @@ -17,6 +17,7 @@ # along with SickRage. If not, see <http://www.gnu.org/licenses/>. import re import sys +import traceback import sickbeard @@ -275,13 +276,6 @@ class UIError(): """ def __init__(self, message): - try: - self.title = sys.exc_info()[1].message - except: - self.title = None + self.title = sys.exc_info()[-2] self.message = message - self.time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') - try: - self.exc_info = sys.exc_info() - except: - self.exc_info = None + self.time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') \ No newline at end of file diff --git a/sickbeard/config.py b/sickbeard/config.py index a1927bbf77ed2cee2571f484a9d357b530ff62bf..fd365ef00f13cd2d09f46369c89f66402c178053 100644 --- a/sickbeard/config.py +++ b/sickbeard/config.py @@ -457,7 +457,8 @@ class ConfigMigrator(): 2: 'Sync backup number with version number', 3: 'Rename omgwtfnzb variables', 4: 'Add newznab catIDs', - 5: 'Metadata update' + 5: 'Metadata update', + 6: 'Convert from XBMC to new KODI variables' } def migrate_config(self): @@ -673,11 +674,11 @@ class ConfigMigrator(): new format: 0|0|0|0|0|0|0|0|0|0 -- 10 places Drop the use of use_banner option. - Migrate the poster override to just using the banner option (applies to kodi only). + Migrate the poster override to just using the banner option (applies to xbmc only). """ - metadata_kodi = check_setting_str(self.config_obj, 'General', 'metadata_kodi', '0|0|0|0|0|0') - metadata_kodi_12plus = check_setting_str(self.config_obj, 'General', 'metadata_kodi_12plus', '0|0|0|0|0|0') + metadata_xbmc = check_setting_str(self.config_obj, 'General', 'metadata_xbmc', '0|0|0|0|0|0') + metadata_xbmc_12plus = check_setting_str(self.config_obj, 'General', 'metadata_xbmc_12plus', '0|0|0|0|0|0') metadata_mediabrowser = check_setting_str(self.config_obj, 'General', 'metadata_mediabrowser', '0|0|0|0|0|0') metadata_ps3 = check_setting_str(self.config_obj, 'General', 'metadata_ps3', '0|0|0|0|0|0') metadata_wdtv = check_setting_str(self.config_obj, 'General', 'metadata_wdtv', '0|0|0|0|0|0') @@ -698,7 +699,7 @@ class ConfigMigrator(): # swap show fanart, show poster cur_metadata[3], cur_metadata[2] = cur_metadata[2], cur_metadata[3] # if user was using use_banner to override the poster, instead enable the banner option and deactivate poster - if metadata_name == 'KODI' and use_banner: + if metadata_name == 'XBMC' and use_banner: cur_metadata[4], cur_metadata[3] = cur_metadata[3], '0' # write new format metadata = '|'.join(cur_metadata) @@ -717,10 +718,27 @@ class ConfigMigrator(): return metadata - sickbeard.METADATA_KODI = _migrate_metadata(metadata_kodi, 'KODI', use_banner) - sickbeard.METADATA_KODI_12PLUS = _migrate_metadata(metadata_kodi_12plus, 'KODI 12+', use_banner) + sickbeard.METADATA_XBMC = _migrate_metadata(metadata_xbmc, 'XBMC', use_banner) + sickbeard.METADATA_XBMC_12PLUS = _migrate_metadata(metadata_xbmc_12plus, 'XBMC 12+', use_banner) sickbeard.METADATA_MEDIABROWSER = _migrate_metadata(metadata_mediabrowser, 'MediaBrowser', use_banner) sickbeard.METADATA_PS3 = _migrate_metadata(metadata_ps3, 'PS3', use_banner) sickbeard.METADATA_WDTV = _migrate_metadata(metadata_wdtv, 'WDTV', use_banner) sickbeard.METADATA_TIVO = _migrate_metadata(metadata_tivo, 'TIVO', use_banner) sickbeard.METADATA_MEDE8ER = _migrate_metadata(metadata_mede8er, 'Mede8er', use_banner) + + # Migration v6: Convert from XBMC to KODI variables + def _migrate_v6(self): + sickbeard.USE_KODI = bool(check_setting_int(self.config_obj, 'XBMC', 'use_xbmc', 0)) + sickbeard.KODI_ALWAYS_ON = bool(check_setting_int(self.config_obj, 'XBMC', 'xbmc_always_on', 1)) + sickbeard.KODI_NOTIFY_ONSNATCH = bool(check_setting_int(self.config_obj, 'XBMC', 'xbmc_notify_onsnatch', 0)) + sickbeard.KODI_NOTIFY_ONDOWNLOAD = bool(check_setting_int(self.config_obj, 'XBMC', 'xbmc_notify_ondownload', 0)) + sickbeard.KODI_NOTIFY_ONSUBTITLEDOWNLOAD = bool(check_setting_int(self.config_obj, 'XBMC', 'xbmc_notify_onsubtitledownload', 0)) + sickbeard.KODI_UPDATE_LIBRARY = bool(check_setting_int(self.config_obj, 'XBMC', 'xbmc_update_library', 0)) + sickbeard.KODI_UPDATE_FULL = bool(check_setting_int(self.config_obj, 'XBMC', 'xbmc_update_full', 0)) + sickbeard.KODI_UPDATE_ONLYFIRST = bool(check_setting_int(self.config_obj, 'XBMC', 'xbmc_update_onlyfirst', 0)) + sickbeard.KODI_HOST = check_setting_str(self.config_obj, 'XBMC', 'xbmc_host', '') + sickbeard.KODI_USERNAME = check_setting_str(self.config_obj, 'XBMC', 'xbmc_username', '', censor_log=True) + sickbeard.KODI_PASSWORD = check_setting_str(self.config_obj, 'XBMC', 'xbmc_password', '', censor_log=True) + sickbeard.METADATA_KODI = check_setting_str(self.config_obj, 'General', 'metadata_xbmc', '0|0|0|0|0|0|0|0|0|0') + sickbeard.METADATA_KODI_12PLUS = check_setting_str(self.config_obj, 'General', 'metadata_xbmc_12plus', '0|0|0|0|0|0|0|0|0|0') + diff --git a/sickbeard/db.py b/sickbeard/db.py index 1d633b08ddc3bf673e3700f414624387d5715fc2..8b119054e1042f77eb7cb2fca089c1600a42f0cc 100644 --- a/sickbeard/db.py +++ b/sickbeard/db.py @@ -73,18 +73,9 @@ class DBConnection(object): raise def _execute(self, query, args): - def convert(x): - if isinstance(x, basestring): - try: - x = unicode(x).decode(sickbeard.SYS_ENCODING) - except: - pass - return x - try: if not args: return self.connection.cursor().execute(query) - # args = map(convert, args) return self.connection.cursor().execute(query, args) except Exception as e: raise e @@ -238,7 +229,10 @@ class DBConnection(object): return columns def _unicode_text_factory(self, x): - return unicode(x, 'utf-8') + try: + return unicode(x, 'utf-8') + except: + return unicode(x, sickbeard.SYS_ENCODING) def _dict_factory(self, cursor, row): d = {} diff --git a/sickbeard/logger.py b/sickbeard/logger.py index 2305025041a1063f0f46b1c8cfae3b40e554808c..323c43c91bc51b1b1b6f034368bd3a1e394ed13a 100644 --- a/sickbeard/logger.py +++ b/sickbeard/logger.py @@ -45,11 +45,16 @@ reverseNames = {u'ERROR': ERROR, censoredItems = {} + class NullHandler(logging.Handler): def emit(self, record): pass -class CensoredFormatter(logging.Formatter): + +class CensoredFormatter(logging.Formatter, object): + def __init__(self, *args, **kwargs): + super(CensoredFormatter, self).__init__(*args, **kwargs) + def format(self, record): msg = super(CensoredFormatter, self).format(record) for k, v in censoredItems.items(): @@ -57,6 +62,7 @@ class CensoredFormatter(logging.Formatter): msg = msg.replace(v, len(v) * '*') return msg + class Logger(object): def __init__(self): self.logger = logging.getLogger('sickrage') @@ -65,7 +71,7 @@ class Logger(object): logging.getLogger('sickrage'), logging.getLogger('tornado.general'), logging.getLogger('tornado.application'), - #logging.getLogger('tornado.access'), + # logging.getLogger('tornado.access'), ] self.consoleLogging = False @@ -119,13 +125,14 @@ class Logger(object): # pass exception information if debugging enabled - kwargs["exc_info"] = 1 if level == ERROR else 0 - self.logger.log(level, message, *args, **kwargs) - if level == ERROR: + self.logger.exception(message, *args, **kwargs) classes.ErrorViewer.add(classes.UIError(message)) - #if sickbeard.GIT_AUTOISSUES: + + # if sickbeard.GIT_AUTOISSUES: # self.submit_errors() + else: + self.logger.log(level, message, *args, **kwargs) def log_error_and_exit(self, error_msg, *args, **kwargs): self.log(error_msg, ERROR, *args, **kwargs) @@ -139,37 +146,33 @@ class Logger(object): if not (sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD and len(classes.ErrorViewer.errors) > 0): return - title = "[APP SUBMITTED]: " - gh_org = sickbeard.GIT_ORG or 'SiCKRAGETV' gh_repo = 'sickrage-issues' - self.gh_issues = Github(login_or_token=sickbeard.GIT_USERNAME, password=sickbeard.GIT_PASSWORD, - user_agent="SiCKRAGE").get_organization(gh_org).get_repo(gh_repo) + gh_issues = Github(login_or_token=sickbeard.GIT_USERNAME, password=sickbeard.GIT_PASSWORD, + user_agent="SiCKRAGE").get_organization(gh_org).get_repo(gh_repo) try: + # read log file if self.logFile and os.path.isfile(self.logFile): with ek.ek(open, self.logFile) as f: log_data = f.readlines() - except Exception as e: - pass + log_data = [line for line in reversed(log_data)] - try: + # parse and submit errors to issue tracker for curError in sorted(classes.ErrorViewer.errors, key=lambda error: error.time, reverse=True)[:500]: if not curError.title: continue - regex = "^(%s)\s*([A-Z]+)\s*(.+?)\s*\:\:\s*(.*)$" % curError.time - - maxlines = 50 pastebin_url = None - for i, x in enumerate(reversed(log_data)): + regex = "^(%s)\s*([A-Z]+)\s*(.+?)\s*\:\:\s*(.*)$" % curError.time + for i, x in enumerate(log_data): x = ek.ss(x) match = re.match(regex, x) if match: level = match.group(2) if reverseNames[level] == ERROR: - paste_data = "".join(log_data[len(log_data) - i - 50:]) + paste_data = "".join(log_data[i:50]) pastebin_url = PastebinAPI().paste('f59b8e9fa1fc2d033e399e6c7fb09d19', paste_data) break @@ -187,14 +190,16 @@ class Logger(object): message += u"---\n" message += u"_STAFF NOTIFIED_: @SiCKRAGETV/owners @SiCKRAGETV/moderators" - issue = self.gh_issues.create_issue(title + curError.title, message) + issue = gh_issues.create_issue("[APP SUBMITTED]: " + curError.title, message) if issue: self.log('Your issue ticket #%s was submitted successfully!' % issue.number) - if not sickbeard.GIT_AUTOISSUES: - ui.notifications.message('Your issue ticket #%s was submitted successfully!' % issue.number) - finally: - classes.ErrorViewer.clear() + # clear error from error list + classes.ErrorViewer.errors.remove(curError) + + return issue + except Exception as e: + self.log(sickbeard.exceptions.ex(e), ERROR) class Wrapper(object): @@ -209,5 +214,6 @@ class Wrapper(object): except AttributeError: return getattr(self.instance, name) + _globals = sys.modules[__name__] = Wrapper(sys.modules[__name__]) diff --git a/sickbeard/metadata/generic.py b/sickbeard/metadata/generic.py index c623be2b5d46b0744c7e1fc6551e8f67e5421abf..0133df688e539b16d954ef6d7eaa45839103968e 100644 --- a/sickbeard/metadata/generic.py +++ b/sickbeard/metadata/generic.py @@ -26,7 +26,7 @@ import re import sickbeard -from sickbeard import exceptions, helpers +from sickbeard import helpers from sickbeard.metadata import helpers as metadata_helpers from sickbeard import logger from sickbeard import encodingKludge as ek @@ -35,6 +35,8 @@ from sickbeard.show_name_helpers import allPossibleShowNames from lib.tmdb_api.tmdb_api import TMDB +import fanart +from fanart.core import Request as fanartRequest class GenericMetadata(): """ @@ -616,6 +618,7 @@ class GenericMetadata(): continue result = result + [self._write_image(seasonData, season_poster_file_path)] + if result: return all(result) else: @@ -664,6 +667,7 @@ class GenericMetadata(): continue result = result + [self._write_image(seasonData, season_banner_file_path)] + if result: return all(result) else: @@ -774,19 +778,27 @@ class GenericMetadata(): if image_type == 'poster_thumb': if getattr(indexer_show_obj, 'poster', None) is not None: image_url = re.sub('posters', '_cache/posters', indexer_show_obj['poster']) + if not image_url: + # Try and get images from Fanart.TV + image_url = self._retrieve_show_images_from_fanart(show_obj, image_type) + if not image_url: + # Try and get images from TMDB + image_url = self._retrieve_show_images_from_tmdb(show_obj, image_type) elif image_type == 'banner_thumb': if getattr(indexer_show_obj, 'banner', None) is not None: image_url = re.sub('graphical', '_cache/graphical', indexer_show_obj['banner']) + if not image_url: + # Try and get images from Fanart.TV + image_url = self._retrieve_show_images_from_fanart(show_obj, image_type) else: if getattr(indexer_show_obj, image_type, None) is not None: image_url = indexer_show_obj[image_type] - - # Try and get posters and fanart from TMDB - if image_url is None: - if image_type in ('poster', 'poster_thumb'): - image_url = self._retrieve_show_images_from_tmdb(show_obj, poster=True) - elif image_type == 'fanart': - image_url = self._retrieve_show_images_from_tmdb(show_obj, backdrop=True) + if not image_url: + # Try and get images from Fanart.TV + image_url = self._retrieve_show_images_from_fanart(show_obj, image_type) + if not image_url: + # Try and get images from TMDB + image_url = self._retrieve_show_images_from_tmdb(show_obj, image_type) if image_url: image_data = metadata_helpers.getShowImage(image_url, which) @@ -961,7 +973,13 @@ class GenericMetadata(): return (indexer_id, name, indexer) - def _retrieve_show_images_from_tmdb(self, show, backdrop=False, poster=False): + def _retrieve_show_images_from_tmdb(self, show, type): + types = {'poster': 'poster_path', + 'banner': None, + 'fanart': 'backdrop_path', + 'poster_thumb': 'poster_path', + 'banner_thumb': None} + # get TMDB configuration info tmdb = TMDB(sickbeard.TMDB_API_KEY) config = tmdb.Configuration() @@ -977,14 +995,41 @@ class GenericMetadata(): try: search = tmdb.Search() for show_name in set(allPossibleShowNames(show)): - for result in search.collection({'query': show_name})['results'] + search.tv({'query': show_name})[ - 'results']: - if backdrop and result['backdrop_path']: - return "{0}{1}{2}".format(base_url, max_size, result['backdrop_path']) - elif poster and result['poster_path']: - return "{0}{1}{2}".format(base_url, max_size, result['poster_path']) + for result in search.collection({'query': show_name})['results'] + search.tv({'query': show_name})['results']: + if types[type] and getattr(result, types[type]): + return "{0}{1}{2}".format(base_url, max_size, result[types[type]]) - except Exception, e: + except Exception as e: + pass + + logger.log(u"Could not find any " + type + " images on TMDB for " + show.name, logger.DEBUG) + + def _retrieve_show_images_from_fanart(self, show, type, thumb=False): + types = {'poster': fanart.TYPE.TV.POSTER, + 'banner': fanart.TYPE.TV.BANNER, + 'poster_thumb': fanart.TYPE.TV.POSTER, + 'banner_thumb': fanart.TYPE.TV.BANNER, + 'fanart': fanart.TYPE.TV.BACKGROUND, + } + + try: + indexerid = helpers.mapIndexersToShow(show)[1] + if indexerid: + request = fanartRequest( + apikey=sickbeard.FANART_API_KEY, + id=indexerid, + ws=fanart.WS.TV, + type=types[type], + sort=fanart.SORT.POPULAR, + limit=fanart.LIMIT.ONE, + ) + + resp = request.response() + url = resp[types[type]][0]['url'] + if thumb: + url = re.sub('/fanart/', '/preview/', url) + return url + except Exception as e: pass - logger.log(u"Could not find any posters or background for " + show.name, logger.DEBUG) + logger.log(u"Could not find any " + type + " images on Fanart.tv for " + show.name, logger.DEBUG) \ No newline at end of file diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py index a320c8d055203c993dd80b98520b0941d4303373..9767498248af156a6d4d6888f3c6be102e0af2a5 100644 --- a/sickbeard/providers/generic.py +++ b/sickbeard/providers/generic.py @@ -24,6 +24,7 @@ import os import re import itertools import urllib + import sickbeard import requests @@ -124,7 +125,9 @@ class GenericProvider: if not self._doLogin(): return - self.headers.update({'Referer': self.proxy.getProxyURL()}) + if self.proxy.isEnabled(): + self.headers.update({'Referer': self.proxy.getProxyURL()}) + return helpers.getURL(self.proxy._buildURL(url), post_data=post_data, params=params, headers=self.headers, timeout=timeout, session=self.session, json=json) diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index d0199584fd8df15de87b3ce65b1d2b004a5bcf02..a9fc6f2aa0ce4b1dc8489057cd516b0715748f3a 100755 --- a/sickbeard/providers/newznab.py +++ b/sickbeard/providers/newznab.py @@ -38,10 +38,6 @@ from sickbeard import logger from sickbeard import tvcache from sickbeard.exceptions import ex, AuthException -from lib import requests -from lib.requests import exceptions -from lib.bencode import bdecode - class NewznabProvider(generic.NZBProvider): def __init__(self, name, url, key='', catIDs='5030,5040', search_mode='eponly', search_fallback=False, enable_daily=False, enable_backlog=False): @@ -118,8 +114,6 @@ class NewznabProvider(generic.NZBProvider): return (False, return_categories, "Error getting html for [%s]" % ("%s/api?%s" % (self.url, '&'.join("%s=%s" % (x,y) for x,y in params.items()) ))) - #xml_categories = helpers.parse_xml(categories) - if not xml_categories: logger.log(u"Error parsing xml for [%s]" % (self.name), logger.DEBUG) diff --git a/sickbeard/show_queue.py b/sickbeard/show_queue.py index 1969fa53770d7631cf04fa1380488226a6ec34e1..3b36c9ae7b76d80e6123313b22589ddf94dd23af 100644 --- a/sickbeard/show_queue.py +++ b/sickbeard/show_queue.py @@ -320,7 +320,7 @@ class QueueItemAdd(ShowQueueItem): return except exceptions.MultipleShowObjectsException: - logger.log(u"The show in " + self.showDir + " is already in your show list, skipping", logger.ERROR) + logger.log(u"The show in " + self.showDir + " is already in your show list, skipping", logger.WARNING) ui.notifications.error('Show skipped', "The show in " + self.showDir + " is already in your show list") self._finishEarly() return @@ -335,7 +335,6 @@ class QueueItemAdd(ShowQueueItem): try: self.show.loadIMDbInfo() except imdb_exceptions.IMDbError, e: - #todo Insert UI notification logger.log(u" Something wrong on IMDb api: " + ex(e), logger.WARNING) except Exception, e: logger.log(u"Error loading IMDb info: " + ex(e), logger.ERROR) diff --git a/sickbeard/tv.py b/sickbeard/tv.py index bc9e44655a49ab89323c561c52116c42904271b2..df39d1af1e7a6bd095f2622fd144590b70dc6dee 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -1686,8 +1686,8 @@ class TVEpisode(object): except (ValueError, IndexError): logger.log(u"Malformed air date of " + str(firstaired) + " retrieved from " + sickbeard.indexerApi( self.indexer).name + " for (" + self.show.name + " - " + str(season) + "x" + str(episode) + ")", - logger.ERROR) - # if I'm incomplete on TVDB but I once was complete then just delete myself from the DB for now + logger.WARNING) + # if I'm incomplete on the indexer but I once was complete then just delete myself from the DB for now if self.indexerid != -1: self.deleteEpisode() return False diff --git a/sickbeard/tvcache.py b/sickbeard/tvcache.py index 283018cd808cb66346f5d2283d4c13ef2d52a777..bd705ec891b173b96eae719824e95218b06e6d42 100644 --- a/sickbeard/tvcache.py +++ b/sickbeard/tvcache.py @@ -139,7 +139,8 @@ class TVCache(): logger.log(traceback.format_exc(), logger.DEBUG) def getRSSFeed(self, url, post_data=None, items=[]): - self.provider.headers.update({'Referer': self.provider.proxy.getProxyURL()}) + if self.provider.proxy.isEnabled(): + self.provider.headers.update({'Referer': self.provider.proxy.getProxyURL()}) return RSSFeeds(self.providerID).getFeed(self.provider.proxy._buildURL(url), post_data, self.provider.headers, items) def _translateTitle(self, title): diff --git a/sickbeard/versionChecker.py b/sickbeard/versionChecker.py index 2e017cc35dd26b0e6b3f9e8169b3c0681185df77..bd15cb2938f64a54a0b90ed76bd59ec249d45275 100644 --- a/sickbeard/versionChecker.py +++ b/sickbeard/versionChecker.py @@ -395,6 +395,11 @@ class GitUpdateManager(UpdateManager): # update remote origin url self.update_remote_origin() + # remove untracked files and performs a hard reset on git branch to avoid update issues + if sickbeard.GIT_RESET: + self.clean() + self.reset() + if self.branch == self._find_installed_branch(): output, err, exit_status = self._run_git(self._git_path, 'pull -f %s %s' % (sickbeard.GIT_REMOTE, self.branch)) # @UnusedVariable else: @@ -406,26 +411,26 @@ class GitUpdateManager(UpdateManager): # Notify update successful if sickbeard.NOTIFY_ON_UPDATE: notifiers.notify_git_update(sickbeard.CUR_COMMIT_HASH if sickbeard.CUR_COMMIT_HASH else "") + return True - else: - # perform a hard reset to try and resolve the issue - if self.reset() and self.update(): - return True - return False + def clean(self): + """ + Calls git clean to remove all untracked files. Returns a bool depending + on the call's success. + """ + output, err, exit_status = self._run_git(self._git_path, 'clean -d -fx ""') # @UnusedVariable + if exit_status == 0: + return True def reset(self): """ Calls git reset --hard to perform a hard reset. Returns a bool depending on the call's success. """ - if sickbeard.GIT_RESET: - output, err, exit_status = self._run_git(self._git_path, 'reset --hard') # @UnusedVariable - - if exit_status == 0: - return True - - return False + output, err, exit_status = self._run_git(self._git_path, 'reset --hard') # @UnusedVariable + if exit_status == 0: + return True def list_remote_branches(self): # update remote origin url diff --git a/sickbeard/webapi.py b/sickbeard/webapi.py index 6508874805b3394bd5d19a4d3bafbdad6cf58caf..f527192c4f44b8ac385f68baaa43088e148a6e82 100644 --- a/sickbeard/webapi.py +++ b/sickbeard/webapi.py @@ -94,13 +94,8 @@ class ApiHandler(RequestHandler): 'image': lambda x: x['image'], } - if sickbeard.USE_API is not True: - accessMsg = u"API :: " + self.request.remote_ip + " - SB API Disabled. ACCESS DENIED" - logger.log(accessMsg, logger.WARNING) - return self.finish(outputCallbackDict['default'](_responds(RESULT_DENIED, msg=accessMsg))) - else: - accessMsg = u"API :: " + self.request.remote_ip + " - gave correct API KEY. ACCESS GRANTED" - logger.log(accessMsg, logger.DEBUG) + accessMsg = u"API :: " + self.request.remote_ip + " - gave correct API KEY. ACCESS GRANTED" + logger.log(accessMsg, logger.DEBUG) # set the original call_dispatcher as the local _call_dispatcher _call_dispatcher = self.call_dispatcher diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index eae29d1a93ba131ab78c484f2ee39abbfba22475..996e3086343ef4203bcbd3fe4b16be9dad08e09e 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -3425,7 +3425,7 @@ class ConfigGeneral(Config): def saveGeneral(self, log_dir=None, web_port=None, web_log=None, encryption_version=None, web_ipv6=None, update_shows_on_start=None, trash_remove_show=None, trash_rotate_logs=None, update_frequency=None, launch_browser=None, web_username=None, - use_api=None, api_key=None, indexer_default=None, timezone_display=None, cpu_preset=None, + api_key=None, indexer_default=None, timezone_display=None, cpu_preset=None, web_password=None, version_notify=None, enable_https=None, https_cert=None, https_key=None, handle_reverse_proxy=None, sort_article=None, auto_update=None, notify_on_update=None, proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None, @@ -3492,7 +3492,6 @@ class ConfigGeneral(Config): if not config.change_LOG_DIR(log_dir, web_log): results += ["Unable to create directory " + os.path.normpath(log_dir) + ", log directory not changed."] - sickbeard.USE_API = config.checkbox_to_value(use_api) sickbeard.API_KEY = api_key sickbeard.ENABLE_HTTPS = config.checkbox_to_value(enable_https) @@ -4713,6 +4712,8 @@ class ErrorLogs(WebRoot): if not (sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD): logger.log(u'Please set your GitHub username and password in the config, unable to submit issue ticket to GitHub!') else: - logger.submit_errors() + issue = logger.submit_errors() + if issue: + ui.notifications.message('Your issue ticket #%s was submitted successfully!' % issue.number) return self.redirect("/errorlogs/") \ No newline at end of file diff --git a/tests/issue_submitter_tests.py b/tests/issue_submitter_tests.py new file mode 100644 index 0000000000000000000000000000000000000000..bb3e29dad5c217e0b1149759c885a2dd6444658e --- /dev/null +++ b/tests/issue_submitter_tests.py @@ -0,0 +1,49 @@ +# coding=UTF-8 +# Author: Dennis Lutter <lad1337@gmail.com> +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of SickRage. +# +# SickRage is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# SickRage is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with SickRage. If not, see <http://www.gnu.org/licenses/>. + +from __future__ import with_statement + +import unittest +import sys, os.path +from configobj import ConfigObj + +sys.path.append(os.path.abspath('..')) +sys.path.append(os.path.abspath('../lib')) + +import sickbeard +import test_lib as test + +def error(): + try: + raise Exception('FAKE EXCEPTION') + except Exception as e: + sickbeard.logger.log("FAKE ERROR: " + sickbeard.exceptions.ex(e), sickbeard.logger.ERROR) + sickbeard.logger.submit_errors() + raise + +class IssueSubmitterBasicTests(unittest.TestCase): + def test_submitter(self): + self.assertRaises(Exception, error) + +if __name__ == "__main__": + print "==================" + print "STARTING - ISSUE SUBMITTER TESTS" + print "==================" + print "######################################################################" + suite = unittest.TestLoader().loadTestsFromTestCase(IssueSubmitterBasicTests) \ No newline at end of file diff --git a/tests/test_lib.py b/tests/test_lib.py index f87d4dc368c17363483f4a158f69ff6e0342ecfe..7f152882d1ab7f4c655124a9764ea849ae43f1c2 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -25,6 +25,7 @@ import sqlite3 import sys import os.path +from configobj import ConfigObj sys.path.append(os.path.abspath('..')) sys.path.append(os.path.abspath('../lib')) @@ -54,9 +55,6 @@ FILEDIR = os.path.join(TESTDIR, SHOWNAME) FILEPATH = os.path.join(FILEDIR, FILENAME) SHOWDIR = os.path.join(TESTDIR, SHOWNAME + " final") -sickbeard.logger.logFile = os.path.join(os.path.join(TESTDIR, 'Logs'), 'test_sickbeard.log') -sickbeard.logger.initLogging() - #================= # prepare env functions #================= @@ -74,6 +72,7 @@ def createTestCacheFolder(): # sickbeard globals #================= sickbeard.SYS_ENCODING = 'UTF-8' + sickbeard.showList = [] sickbeard.QUALITY_DEFAULT = 4 # hdtv sickbeard.FLATTEN_FOLDERS_DEFAULT = 0 @@ -90,13 +89,23 @@ sickbeard.providerList = providers.makeProviderList() sickbeard.PROG_DIR = os.path.abspath('..') sickbeard.DATA_DIR = sickbeard.PROG_DIR +sickbeard.CONFIG_FILE = os.path.join(sickbeard.DATA_DIR, "config.ini") +sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE) + +sickbeard.BRANCG = sickbeard.config.check_setting_str(sickbeard.CFG, 'General', 'branch', '') +sickbeard.CUR_COMMIT_HASH = sickbeard.config.check_setting_str(sickbeard.CFG, 'General', 'cur_commit_hash', '') +sickbeard.GIT_USERNAME = sickbeard.config.check_setting_str(sickbeard.CFG, 'General', 'git_username', '') +sickbeard.GIT_PASSWORD = sickbeard.config.check_setting_str(sickbeard.CFG, 'General', 'git_password', '', censor_log=True) + sickbeard.LOG_DIR = os.path.join(TESTDIR, 'Logs') +sickbeard.logger.logFile = os.path.join(sickbeard.LOG_DIR, 'test_sickbeard.log') createTestLogFolder() -sickbeard.logger.initLogging(False) sickbeard.CACHE_DIR = os.path.join(TESTDIR, 'cache') createTestCacheFolder() +sickbeard.logger.initLogging(False, True) + #================= # dummy functions #================= diff --git a/tests/torrent_tests.py b/tests/torrent_tests.py index b076cb44572d9ed92c3c28bde357c5226906d2a4..f6c84afa5824fbbb0fe0af9e8dd559fc8a85b533 100644 --- a/tests/torrent_tests.py +++ b/tests/torrent_tests.py @@ -21,11 +21,11 @@ from __future__ import with_statement import unittest import sys, os.path -import urlparse sys.path.append(os.path.abspath('..')) sys.path.append(os.path.abspath('../lib')) +import urlparse import test_lib as test from bs4 import BeautifulSoup from sickbeard.helpers import getURL