Add "Maximum fanart image files per show to cache" to config General/Interface. Add populate images when the daily show updater is run with a default maximum 3 images per show. Change force full update in a show will replace existing images with new. Add fanart livepanel to lower right of Episodes View and Display Show page. Add highlight panel red until button is clicked a few times. Add flick through multiple background images on Episodes View and Display Show page. Add persistent move poster image to right hand side or hide on Display Show page (multi-click the eye). Add persistent translucency of background images on Episodes View and Display Show page. Add persistent fanart rating to avoid art completely, random display, random from a group, or display fave always. Add persistent views of the show detail on Display Show page. Add persistent views on Episodes View. Add persistent button to collapse and expand card images on Episode View/Layout daybyday. Add non persistent "Open gear" and "Full fanart" image views to Episodes View and Display Show page. Add "smart" selection of fanart image to display on Episode view. Change insert [!] and change text shade of ended shows in drop down show list on Display Show page. Change button graphic for next and previous show of show list on Display Show page. Add logic to hide some livepanel buttons until artwork becomes available or in other circumstances. Add "(Ended)" where appropriate to show title on Display Show page. Add links to fanart.tv where appropriate on Display Show page. Change use tense for label "Airs" or "Aired" depending on if show ended. Change display "No files" instead of "0 files" and "Upgrade once" instead of "End upgrade on first match". Add persistent button to newest season to "Show all" episodes. Add persistent button to all shown seasons to "Hide most" episodes. Add button to older seasons to toggle "Show Season n" or "Show Specials" with "Hide..." episodes. Add season level status counts next to each season header on display show page Add sorting to season table headers on display show page Add filename and size to quality badge on display show page, removed its redundant "downloaded" text Remove redundant "Add show" buttons Change combine the NFO and TBN columns into a single Meta column Change reduce screen estate used by episode numbers columns Change improve clarity of text on Add Show page. Add "Reset fanart ratings" to show Edit/Other tab. Add fanart usage to show Edit/Other tab. Add fanart keys guide to show Edit/Other tab. Change add placeholder tip to "Alternative release name(s)" on show Edit. Change add placeholder tip to search box on shows Search. Change hide Anime tips on show Edit when selecting its mutually exclusive options. Change label "End upgrade on first match" to "Upgrade once" on show Edit. Change improve performance rendering displayShow. Add total episodes to start of show description (excludes specials if those are hidden). Add "Add show" actions i.e. "Search", "Trakt cards", "IMDb cards", and "Anime" to Shows menu. Add "Import (existing)" action to Tools menu. Change SD quality from red to dark green, 2160p UHD 4K is red. Change relocate the functions of Logs & Errors to the right side Tools menu -> View Log File. Add warning indicator to the Tools menu in different colour depending on error count (green through red). Change View Log error item output from reversed to natural order. Change View Log add a typeface and some colour to improve readability. Change View Log/Errors only display "Clear Errors" button when there are errors to clear. Change improve performance of View Log File.pull/708/head
After Width: | Height: | Size: 3.4 MiB |
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 205 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 306 B |
After Width: | Height: | Size: 691 B |
After Width: | Height: | Size: 250 B |
After Width: | Height: | Size: 477 B |
After Width: | Height: | Size: 496 B |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 436 B After Width: | Height: | Size: 531 B |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 263 B After Width: | Height: | Size: 693 B |
@ -0,0 +1,125 @@ |
|||
#import datetime |
|||
#import sickbeard |
|||
#from sickbeard import network_timezones, sbdatetime, subtitles |
|||
#from sickbeard.common import Overview, Quality, statusStrings, ARCHIVED, UNAIRED, SUBTITLED |
|||
#from lib import subliminal |
|||
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp# |
|||
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp# |
|||
####### |
|||
#set $network_timezone = $network_timezones.get_network_timezone($show.network) |
|||
#set $network_time = $network_timezones.parse_time($show.airs) |
|||
#set $odd = 0 |
|||
#set $fuzzydate = 'airdate' |
|||
#set $scene, $scene_anime = (False, False) |
|||
#if not $show.air_by_date and not $show.is_sports and not $show.is_anime and $show.is_scene |
|||
#set $scene = True |
|||
#elif not $show.air_by_date and not $show.is_sports and $show.is_anime and $show.is_scene |
|||
#set $scene_anime = True |
|||
#end if |
|||
#set $attr_title_ep = ('', ' (Anime #)')[$show.is_anime] |
|||
## |
|||
#set $eps = $getVar('episodes', None) or [$episode] |
|||
#for $ep in $eps |
|||
#set $ep_str = '%sx%s' % ($ep['season'], $ep['episode']) |
|||
#set $epLoc = $ep['location'] |
|||
#set never_aired = 0 < int($ep['season']) and 1 == int($ep['airdate']) |
|||
<tr class="#echo ($Overview.overviewStrings[$ep_cats[$ep_str]], 'airdate-never')[$never_aired]##echo ('', ' archived')[ARCHIVED == int($ep['status'])]#"> |
|||
<td class="col-checkbox"> |
|||
#if $UNAIRED != int($ep['status']) and not $never_aired |
|||
<input type="checkbox" class="epCheck" id="$ep_str" name="$ep_str"> |
|||
#end if |
|||
</td> |
|||
|
|||
#set $nfo, $nfo_img = (('No', '-no'), ('Yes', ''))[int($ep['hasnfo'])] |
|||
#set $tbn, $tbn_img = (('No', '-no'), ('Yes', ''))[int($ep['hastbn'])] |
|||
<td align="center" class="meta"><img src="$sbRoot/images/nfo${nfo_img}.gif" alt="$nfo" title="$nfo" width="23" height="11" /><br /> |
|||
<img src="$sbRoot/images/tbn${tbn_img}.gif" alt="$tbn" title="$tbn" width="23" height="11" /></td> |
|||
|
|||
#if $epLoc and $show._location and $epLoc.lower().startswith($show._location.lower()) |
|||
#set $epLoc = $epLoc[len($show._location)+1:] |
|||
#elif $epLoc and (not $epLoc.lower().startswith($show._location.lower()) or not $show._location) |
|||
#set $epLoc = $epLoc |
|||
#end if |
|||
|
|||
#set $episode = str($ep['episode']) + ('', ' (%s)' % $ep['absolute_number'])[$show.is_anime] |
|||
#set $downloaded = ('', '<strong>file</strong> %s<br /><strong>size</strong> %s' % ($epLoc, $sickbeard.helpers.human($ep['file_size'])))['' != $epLoc and None != $epLoc] |
|||
#set $attr=(('', 'Ep # %s' % $attr_title_ep)[bool($attr_title_ep)], $downloaded)[any($downloaded)] |
|||
<td align="center"> |
|||
<span style="white-space:nowrap"#if $attr# class="addQTip" title="$attr"#end if#>$episode</span> |
|||
</td> |
|||
|
|||
#slurp |
|||
#if $scene |
|||
#set $dfltSeas, $dfltEpis = (0, 0) if ($ep['season'], $ep['episode']) not in $xem_numbering else $xem_numbering[($ep['season'], $ep['episode'])] |
|||
<td align="center"> |
|||
<input type="text" placeholder="#echo '%sx%s' % ($dfltSeas, $dfltEpis)#" size="6" maxlength="8" |
|||
class="sceneSeasonXEpisode form-control input-scene" data-for-season="$ep['season']" data-for-episode="$ep['episode']" |
|||
id="sceneSeasonXEpisode_#echo '%s_%s_%s' % ($show.indexerid, $ep['season'], $ep['episode'])#" |
|||
title="Change the value here if scene numbering differs from the indexer episode numbering" |
|||
#if ($ep['season'], $ep['episode']) in $scene_numbering |
|||
#set $scSeas, $scEpis = $scene_numbering[($ep['season'], $ep['episode'])] |
|||
value="#echo '%sx%s' % ($scSeas, $scEpis)#" |
|||
#else |
|||
value="" |
|||
#end if |
|||
style="padding:0; text-align:center; max-width:60px"> |
|||
</td> |
|||
#elif $scene_anime |
|||
#set $dfltAbsolute = 0 if $ep['absolute_number'] not in $xem_absolute_numbering else $xem_absolute_numbering[$ep['absolute_number']] |
|||
<td align="center"> |
|||
<input type="text" placeholder="$dfltAbsolute" size="6" maxlength="8" |
|||
class="sceneAbsolute form-control input-scene" data-for-absolute="$ep['absolute_number']" |
|||
id="sceneAbsolute_#echo '%s_%s' % ($show.indexerid, $ep['absolute_number'])#" |
|||
title="Change the value here if scene absolute numbering differs from the indexer absolute numbering" |
|||
#if $ep['absolute_number'] in $scene_absolute_numbering |
|||
value="$scene_absolute_numbering[$ep['absolute_number']]" |
|||
#else |
|||
value="" |
|||
#end if |
|||
style="padding:0; text-align:center; max-width:60px" /> |
|||
</td> |
|||
#end if |
|||
#slurp |
|||
<td class="col-name"> |
|||
<img src="$sbRoot/images/info32.png" width="16" height="16" alt="" class="plotInfo#echo '%s" />' %\ |
|||
('None opacity40', ('" id="plot_info_%s_%s_%s' % ($show.indexerid, $ep['season'], $ep['episode'])))[None is not $ep['description'] and '' != $ep['description']]# |
|||
#if not $ep['name'] or 'TBA' == $ep['name']#<em class="tba grey-text">TBA</em>#else#$ep['name']#end if# |
|||
</td> |
|||
|
|||
<td class="col-airdate"> |
|||
<span class="$fuzzydate" data-airdate="$ep['airdate']"#if $sg_var('FUZZY_DATING')# data-fulldate="$sbdatetime.sbdatetime.sbfdate(dt=$datetime.date.fromordinal($ep['airdate']), d_preset='%A, %B %d, %Y')"#end if#>#if 1 == int($ep['airdate']) then 'never' else $sbdatetime.sbdatetime.sbfdate($sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($ep['airdate'], $network_time, $network_timezone)))#</span> |
|||
</td> |
|||
|
|||
#if $sg_var('USE_SUBTITLES') and $show.subtitles |
|||
<td class="col-subtitles" align="center"> |
|||
#if $ep['subtitles'] |
|||
#for $sub_lang in subliminal.language.language_list($ep['subtitles'].split(',')) |
|||
#if '' != sub_lang.alpha2 |
|||
<img src="$sbRoot/images/flags/${sub_lang.alpha2}.png" width="16" height="11" alt="${sub_lang}" /> |
|||
#end if |
|||
#end for |
|||
#end if |
|||
</td> |
|||
#end if |
|||
#slurp |
|||
#set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($ep['status'])) |
|||
#if Quality.NONE != $curQuality |
|||
<td class="col-status">#if SUBTITLED == $curStatus#<span class="addQTip" title="$statusStrings[$curStatus]"><i class="sgicon-subtitles" style="vertical-align:middle"></i></span>#else#$statusStrings[$curStatus].replace('Downloaded', '')#end if# <span class="quality $Quality.get_quality_css($curQuality)#if $downloaded# addQTip" title="$downloaded#end if#">$Quality.qualityStrings[$curQuality]</span></td> |
|||
#else |
|||
<td class="col-status">$statusStrings[$curStatus]</td> |
|||
#end if |
|||
<td class="col-search"> |
|||
#if 0 != int($ep['season']) |
|||
#if (int($ep['status']) in $Quality.SNATCHED or int($ep['status']) in $Quality.DOWNLOADED) and $sg_var('USE_FAILED_DOWNLOADS') |
|||
<a class="epRetry" id="$ep_str" name="$ep_str" href="$sbRoot/home/retryEpisode?show=$show.indexerid&season=$ep['season']&episode=$ep['episode']"><img src="$sbRoot/images/search16.png" height="16" alt="retry" title="Retry download" /></a> |
|||
#else |
|||
<a class="epSearch" id="$ep_str" name="$ep_str" href="$sbRoot/home/searchEpisode?show=$show.indexerid&season=$ep['season']&episode=$ep['episode']"><img src="$sbRoot/images/search16.png" width="16" height="16" alt="search" title="Manual search" /></a> |
|||
#end if |
|||
#end if |
|||
#slurp |
|||
#if $sg_var('USE_SUBTITLES') and $show.subtitles and len(set(str($ep['subtitles']).split(',')).intersection(set($subtitles.wantedLanguages()))) < len($subtitles.wantedLanguages()) and $ep['location'] |
|||
<a class="epSubtitlesSearch" href="$sbRoot/home/searchEpisodeSubtitles?show=$show.indexerid&season=$ep['season']&episode=$ep['episode']"><img src="$sbRoot/images/closed_captioning.png" height="16" alt="search subtitles" title="Search subtitles" /></a> |
|||
#end if |
|||
</td> |
|||
</tr> |
|||
#end for |
@ -0,0 +1,59 @@ |
|||
#import sickbeard |
|||
#from sickbeard.helpers import anon_url |
|||
<% def mainvar(varname, default=False): return getattr(sickbeard, varname, default) %> |
|||
<% def mainstr(varname, default=''): return getattr(sickbeard, varname, default) %> |
|||
## |
|||
#set $panel_title = {'viewart0': 'Poster <em>left</em>', 'viewart1': 'Poster <em>right</em>', 'viewart2': 'No poster', |
|||
'viewart3': 'Open gear', 'viewart4': 'Backart only', |
|||
'translucent_on': 'Translucency <em>on</em>', 'translucent_off': 'Translucency <em>off</em>', |
|||
'rateart0': 'Random (default)', 'rateart1': 'Group random', |
|||
'rateart2': 'Always display', 'rateart3': 'Avoid image', |
|||
'backart_on': 'Backart <em>on</em>', 'backart_off': 'Backart <em>off</em>', |
|||
'viewmode0': 'Regular view', 'viewmode1': 'Proview I', 'viewmode2': 'Proview II', |
|||
'viewmode3': 'Set/Save art random/avoids'} |
|||
#set $init_title_translucent = $panel_title['translucent_' + ('off', 'on')[$mainvar('DISPLAY_SHOW_BACKGROUND_TRANSLUCENT')]] |
|||
#set $init_title_backart = $panel_title['backart_' + ('off', 'on')[$mainvar('DISPLAY_SHOW_BACKGROUND') and $has_art]] |
|||
#set $init_title_view = $panel_title['viewmode%s' % ((1, 0)[not $mainvar('DISPLAY_SHOW_VIEWMODE')], $mainvar('DISPLAY_SHOW_VIEWMODE'))[$mainvar('DISPLAY_SHOW_BACKGROUND') and $has_art]] |
|||
## |
|||
<script> |
|||
config.panelTitles = $panel_title; |
|||
</script> |
|||
<script type="text/javascript" src="$sbRoot/js/livepanel.js?v=$sbPID"></script> |
|||
|
|||
<div id="livepanel" class="off $getVar('fanart_panel', 'highlight2')"> |
|||
<span class="over-layer0"> |
|||
<span class="art-toggle oneof"> |
|||
<i class="icon-glyph"></i> |
|||
<i class="icon-glyph"></i> |
|||
</span> |
|||
<span class="art-toggle-all"><i class="icon-glyph"></i></span> |
|||
<span class="art-toggle"> |
|||
<i class="icon-glyph"></i> |
|||
<i class="icon-glyph rate-art"></i> |
|||
</span> |
|||
<i class="icon-glyph"></i> |
|||
<span class="art-toggle-all"><i class="icon-glyph last"></i></span> |
|||
</span> |
|||
<span id="viewmodes" class="over-layer1"> |
|||
<span class="art-toggle oneof"> |
|||
<a id="art-next" title="<span style='white-space:nowrap'>Next view</span>" href="#"><i class="icon-glyph"></i></a> |
|||
<a id="art-prev" title="<span style='white-space:nowrap'>Previous view</span>" href="#"><i class="icon-glyph"></i></a> |
|||
</span> |
|||
<span class="art-toggle-all"><a id="viewart" title="Poster left" href="#"><i class="icon-glyph"></i></a></span> |
|||
<span class="art-toggle"> |
|||
<a id="translucent" title="$init_title_translucent" href="#"><i class="icon-glyph"></i></a> |
|||
<a id="rate-art" title="Random (default)" href="#"><i class="icon-glyph"></i></a> |
|||
</span> |
|||
#if $has_art |
|||
<a id="back-art" title="$init_title_backart" href="#"><i class="icon-glyph image"></i></a> |
|||
#else |
|||
#try |
|||
#set $link = $anon_url('https://fanart.tv/?s=', $clean_show_name, '§=1') |
|||
<a id="back-art" title="No art! Force full update or add one to fanart.tv if none available" href="$link" rel="noreferrer" onclick="window.open(this.href, '_blank'); return !1;"><i class="icon-glyph fatv"></i></a> |
|||
#except |
|||
<a id="back-art" title="No art available!" href="#"><i class="icon-glyph image"></i></a> |
|||
#end try |
|||
#end if |
|||
<span class="art-toggle-all"><a id="proview" title="$init_title_view" href="#"><i class="icon-glyph"></i></a></span> |
|||
</span> |
|||
</div> |
@ -1,264 +1,317 @@ |
|||
$(document).ready(function () { |
|||
|
|||
$('#sbRoot').ajaxEpSearch({'colorRow': true}); |
|||
|
|||
$('#sbRoot').ajaxEpSubtitlesSearch(); |
|||
|
|||
$('#seasonJump').change(function () { |
|||
var id = $(this).val(); |
|||
if (id && id != 'jump') { |
|||
$('html,body').animate({scrollTop: $(id).offset().top}, 'slow'); |
|||
location.hash = id; |
|||
} |
|||
$(this).val('jump'); |
|||
}); |
|||
|
|||
$('#prevShow, #nextShow').click(function () { |
|||
var select$ = $('#pickShow'), |
|||
index = $.inArray(select$.find('option:selected').val()*1, TVShowList); |
|||
select$.find('option[value="' + TVShowList[('nextShow' === $(this).attr('id') |
|||
? (index < TVShowList.length - 1 ? index + 1 : 0) |
|||
: (0 < index ? index - 1 : TVShowList.length - 1))] + '"]').attr('selected', 'selected'); |
|||
select$.change(); |
|||
return false; |
|||
}); |
|||
|
|||
$('#changeStatus').click(function () { |
|||
var sbRoot = $('#sbRoot').val(); |
|||
var epArr = new Array() |
|||
|
|||
$('.epCheck').each(function () { |
|||
|
|||
if (this.checked == true) { |
|||
epArr.push($(this).attr('id')) |
|||
} |
|||
|
|||
}); |
|||
|
|||
if (epArr.length == 0) |
|||
return false; |
|||
|
|||
url = sbRoot + '/home/setStatus?show=' + $('#showID').attr('value') + '&eps=' + epArr.join('|') + '&status=' + $('#statusSelect').attr('value'); |
|||
window.location.href = url |
|||
|
|||
}); |
|||
|
|||
$('.seasonCheck').click(function () { |
|||
var seasCheck = this; |
|||
var seasNo = $(seasCheck).attr('id'); |
|||
|
|||
$('.epCheck:visible').each(function () { |
|||
var epParts = $(this).attr('id').split('x'); |
|||
|
|||
if (epParts[0] == seasNo) { |
|||
this.checked = seasCheck.checked |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
var lastCheck = null; |
|||
$('.epCheck').click(function (event) { |
|||
|
|||
if (!lastCheck || !event.shiftKey) { |
|||
lastCheck = this; |
|||
return; |
|||
} |
|||
|
|||
var check = this; |
|||
var found = 0; |
|||
|
|||
$('.epCheck').each(function () { |
|||
switch (found) { |
|||
case 2: |
|||
return false; |
|||
case 1: |
|||
this.checked = lastCheck.checked; |
|||
} |
|||
|
|||
if (this == check || this == lastCheck) |
|||
found++; |
|||
}); |
|||
|
|||
lastClick = this; |
|||
}); |
|||
|
|||
// selects all visible episode checkboxes.
|
|||
$('.seriesCheck').click(function () { |
|||
$('.epCheck:visible').each(function () { |
|||
this.checked = true |
|||
}); |
|||
$('.seasonCheck:visible').each(function () { |
|||
this.checked = true |
|||
}) |
|||
}); |
|||
|
|||
// clears all visible episode checkboxes and the season selectors
|
|||
$('.clearAll').click(function () { |
|||
$('.epCheck:visible').each(function () { |
|||
this.checked = false |
|||
}); |
|||
$('.seasonCheck:visible').each(function () { |
|||
this.checked = false |
|||
}); |
|||
}); |
|||
|
|||
// handle the show selection dropbox
|
|||
$('#pickShow').change(function () { |
|||
var sbRoot = $('#sbRoot').val(); |
|||
var val = $(this).attr('value'); |
|||
if (val == 0) |
|||
return; |
|||
url = sbRoot + '/home/displayShow?show=' + val; |
|||
window.location.href = url |
|||
}); |
|||
|
|||
// show/hide different types of rows when the checkboxes are changed
|
|||
$("#checkboxControls input").change(function (e) { |
|||
var whichClass = $(this).attr('id'); |
|||
$(this).showHideRows(whichClass); |
|||
}); |
|||
|
|||
// initially show/hide all the rows according to the checkboxes
|
|||
$("#checkboxControls input").each(function (e) { |
|||
var status = this.checked; |
|||
$("tr." + $(this).attr('id')).each(function (e) { |
|||
if (status) { |
|||
$(this).show(); |
|||
} else { |
|||
$(this).hide(); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
$.fn.showHideRows = function (whichClass) { |
|||
|
|||
var status = $('#checkboxControls > input, #' + whichClass).prop('checked'); |
|||
$("tr." + whichClass).each(function (e) { |
|||
if (status) { |
|||
$(this).show(); |
|||
} else { |
|||
$(this).hide(); |
|||
} |
|||
}); |
|||
|
|||
// hide season headers with no episodes under them
|
|||
$('tr.seasonheader').each(function () { |
|||
var numRows = 0; |
|||
var seasonNo = $(this).attr('id'); |
|||
$('tr.' + seasonNo + ' :visible').each(function () { |
|||
numRows++ |
|||
}); |
|||
if (numRows == 0) { |
|||
$(this).hide(); |
|||
$('#' + seasonNo + '-cols').hide() |
|||
} else { |
|||
$(this).show(); |
|||
$('#' + seasonNo + '-cols').show() |
|||
} |
|||
|
|||
}); |
|||
}; |
|||
|
|||
function setEpisodeSceneNumbering(forSeason, forEpisode, sceneSeason, sceneEpisode) { |
|||
var sbRoot = $('#sbRoot').val(); |
|||
var showId = $('#showID').val(); |
|||
var indexer = $('#indexer').val(); |
|||
|
|||
if (sceneSeason === '') sceneSeason = null; |
|||
if (sceneEpisode === '') sceneEpisode = null; |
|||
|
|||
$.getJSON(sbRoot + '/home/setSceneNumbering', |
|||
{ |
|||
'show': showId, |
|||
'indexer': indexer, |
|||
'forSeason': forSeason, |
|||
'forEpisode': forEpisode, |
|||
'sceneSeason': sceneSeason, |
|||
'sceneEpisode': sceneEpisode |
|||
}, |
|||
function (data) { |
|||
// Set the values we get back
|
|||
if (data.sceneSeason === null || data.sceneEpisode === null) { |
|||
$('#sceneSeasonXEpisode_' + showId + '_' + forSeason + '_' + forEpisode).val(''); |
|||
} |
|||
else { |
|||
$('#sceneSeasonXEpisode_' + showId + '_' + forSeason + '_' + forEpisode).val(data.sceneSeason + 'x' + data.sceneEpisode); |
|||
} |
|||
if (!data.success) { |
|||
if (data.errorMessage) { |
|||
alert(data.errorMessage); |
|||
} else { |
|||
alert('Update failed.'); |
|||
} |
|||
} |
|||
} |
|||
); |
|||
} |
|||
|
|||
function setAbsoluteSceneNumbering(forAbsolute, sceneAbsolute) { |
|||
var sbRoot = $('#sbRoot').val(); |
|||
var showId = $('#showID').val(); |
|||
var indexer = $('#indexer').val(); |
|||
|
|||
if (sceneAbsolute === '') sceneAbsolute = null; |
|||
|
|||
$.getJSON(sbRoot + '/home/setSceneNumbering', |
|||
{ |
|||
'show': showId, |
|||
'indexer': indexer, |
|||
'forAbsolute': forAbsolute, |
|||
'sceneAbsolute': sceneAbsolute |
|||
}, |
|||
function (data) { |
|||
// Set the values we get back
|
|||
if (data.sceneAbsolute === null) { |
|||
$('#sceneAbsolute_' + showId + '_' + forAbsolute).val(''); |
|||
} |
|||
else { |
|||
$('#sceneAbsolute_' + showId + '_' + forAbsolute).val(data.sceneAbsolute); |
|||
} |
|||
if (!data.success) { |
|||
if (data.errorMessage) { |
|||
alert(data.errorMessage); |
|||
} else { |
|||
alert('Update failed.'); |
|||
} |
|||
} |
|||
} |
|||
); |
|||
} |
|||
|
|||
$('.sceneSeasonXEpisode').change(function () { |
|||
// Strip non-numeric characters
|
|||
$(this).val($(this).val().replace(/[^0-9xX]*/g, '')); |
|||
var forSeason = $(this).attr('data-for-season'); |
|||
var forEpisode = $(this).attr('data-for-episode'); |
|||
var showId = $('#showID').val(); |
|||
var indexer = $('#indexer').val(); |
|||
|
|||
//var sceneEpisode = $('#sceneEpisode_' + showId + '_' + forSeason +'_' + forEpisode).val();
|
|||
var m = $(this).val().match(/^(\d+)x(\d+)$/i); |
|||
var sceneSeason = null, sceneEpisode = null; |
|||
if (m) { |
|||
sceneSeason = m[1]; |
|||
sceneEpisode = m[2]; |
|||
} |
|||
setEpisodeSceneNumbering(forSeason, forEpisode, sceneSeason, sceneEpisode); |
|||
}); |
|||
|
|||
$('.sceneAbsolute').change(function () { |
|||
// Strip non-numeric characters
|
|||
$(this).val($(this).val().replace(/[^0-9xX]*/g, '')); |
|||
var forAbsolute = $(this).attr('data-for-absolute'); |
|||
var showId = $('#showID').val(); |
|||
var indexer = $('#indexer').val(); |
|||
|
|||
var m = $(this).val().match(/^(\d{1,3})$/i); |
|||
var sceneAbsolute = null; |
|||
if (m) { |
|||
sceneAbsolute = m[1]; |
|||
} |
|||
setAbsoluteSceneNumbering(forAbsolute, sceneAbsolute); |
|||
}); |
|||
|
|||
}); |
|||
/** @namespace $.SickGear.Root */ |
|||
/** @namespace config.TVShowList */ |
|||
/** @namespace config.useIMDbInfo */ |
|||
/** @namespace $.SickGear.config.useFuzzy */ |
|||
/** @namespace $.SickGear.config.dateFormat */ |
|||
/** @namespace $.SickGear.config.timeFormat */ |
|||
/** @namespace $.SickGear.config.fuzzyTrimZero */ |
|||
$(document).ready(function() { |
|||
|
|||
// handle the show selection dropbox
|
|||
$('#pickShow').change(function() { |
|||
var val = $(this).attr('value'); |
|||
if (val != 0) |
|||
window.location.href = $.SickGear.Root + '/home/displayShow?show=' + val; |
|||
}); |
|||
|
|||
$('#prevShow, #nextShow').on('click', function() { |
|||
var select$ = $('#pickShow'), |
|||
index = $.inArray(select$.find('option:selected').val()*1, config.TVShowList); |
|||
//noinspection JSUnresolvedVariable
|
|||
select$.find('option[value="' + config.TVShowList[('nextShow' === $(this).attr('id') |
|||
? (index < config.TVShowList.length - 1 ? index + 1 : 0) |
|||
: (0 < index ? index - 1 : config.TVShowList.length - 1))] + '"]').attr('selected', 'selected'); |
|||
select$.change(); |
|||
return !1; |
|||
}); |
|||
|
|||
$('#seasonJump').change(function() { |
|||
var id = $(this).val(); |
|||
if (id && 'jump' != id) { |
|||
$('html,body').animate({scrollTop: $(id).offset().top}, 'slow'); |
|||
location.hash = id; |
|||
} |
|||
$(this).val('jump'); |
|||
}); |
|||
|
|||
$('.details-plot').collapser({ |
|||
mode: 'lines', |
|||
truncate: 10, |
|||
showText: '<span class="pull-right moreless"><i class="sgicon-arrowdown" style="margin-right:2px"></i>more</span>', |
|||
hideText: '<span class="pull-right moreless"><i class="sgicon-arrowup" style="margin-right:2px"></i>less</span>', |
|||
showClass: 'show-class' |
|||
}); |
|||
|
|||
if (config.useIMDbInfo){ |
|||
$.fn.generateStars = function() { |
|||
return this.each(function(i,e){$(e).html($('<span/>').width($(e).text()*12));}); |
|||
}; |
|||
$('.imdbstars').generateStars(); |
|||
} |
|||
|
|||
$('#changeStatus').on('click', function() { |
|||
var epArr = []; |
|||
|
|||
$('.epCheck').each(function() { |
|||
this.checked && epArr.push($(this).attr('id')) |
|||
}); |
|||
if (epArr.length) |
|||
window.location.href = $.SickGear.Root + '/home/setStatus?show=' + $('#showID').attr('value') + |
|||
'&eps=' + epArr.join('|') + '&status=' + $('#statusSelect').attr('value'); |
|||
}); |
|||
|
|||
// show/hide different types of rows when the checkboxes are changed
|
|||
var el = $('#checkboxControls').find('input'); |
|||
el.change(function() { |
|||
$(this).showHideRows($(this).attr('id')); |
|||
}); |
|||
|
|||
// initially show/hide all the rows according to the checkboxes
|
|||
el.each(function() { |
|||
var status = this.checked; |
|||
$('tr.' + $(this).attr('id')).each(function() { |
|||
status && $(this).show() || $(this).hide(); |
|||
}); |
|||
}); |
|||
|
|||
$.fn.showHideRows = function(whichClass) { |
|||
|
|||
var status = $('#checkboxControls > input, #' + whichClass).prop('checked'); |
|||
$('tr.' + whichClass).each(function() { |
|||
status && $(this).show() || $(this).hide(); |
|||
}); |
|||
|
|||
// hide season headers with no episodes under them
|
|||
$('tr.seasonheader').each(function() { |
|||
var numRows = 0; |
|||
var seasonNo = $(this).attr('id'); |
|||
$('tr.' + seasonNo + ' :visible').each(function() { |
|||
numRows++ |
|||
}); |
|||
var el = $('#' + seasonNo + '-cols'); |
|||
if (0 == numRows) { |
|||
$(this).hide(); |
|||
el.hide(); |
|||
} else { |
|||
$(this).show(); |
|||
el.show(); |
|||
} |
|||
|
|||
}); |
|||
}; |
|||
|
|||
function checkState(state){ |
|||
$('.epCheck:visible, .seasonCheck:visible').prop('checked', state) |
|||
} |
|||
// selects all visible episode checkboxes.
|
|||
$('.seriesCheck').on('click', function() { checkState(!0); }); |
|||
|
|||
// clears all visible episode checkboxes and the season selectors
|
|||
$('.clearAll').on('click', function() { checkState(!1); }); |
|||
|
|||
function setEpisodeSceneNumbering(forSeason, forEpisode, sceneSeason, sceneEpisode) { |
|||
var showId = $('#showID').val(), indexer = $('#indexer').val(); |
|||
|
|||
if ('' === sceneSeason) sceneSeason = null; |
|||
if ('' === sceneEpisode) sceneEpisode = null; |
|||
|
|||
$.getJSON($.SickGear.Root + '/home/setSceneNumbering', |
|||
{ |
|||
'show': showId, |
|||
'indexer': indexer, |
|||
'forSeason': forSeason, |
|||
'forEpisode': forEpisode, |
|||
'sceneSeason': sceneSeason, |
|||
'sceneEpisode': sceneEpisode |
|||
}, |
|||
function(data) { |
|||
// Set the values we get back
|
|||
$('#sceneSeasonXEpisode_' + showId + '_' + forSeason + '_' + forEpisode).val( |
|||
(null === data.sceneSeason || null === data.sceneEpisode) |
|||
? '' : data.sceneSeason + 'x' + data.sceneEpisode); |
|||
if (!data.success) |
|||
alert(data.errorMessage ? data.errorMessage : 'Update failed.'); |
|||
} |
|||
); |
|||
} |
|||
|
|||
function setAbsoluteSceneNumbering(forAbsolute, sceneAbsolute) { |
|||
var showId = $('#showID').val(), indexer = $('#indexer').val(); |
|||
|
|||
if ('' === sceneAbsolute) |
|||
sceneAbsolute = null; |
|||
|
|||
$.getJSON($.SickGear.Root + '/home/setSceneNumbering', |
|||
{ |
|||
'show': showId, |
|||
'indexer': indexer, |
|||
'forAbsolute': forAbsolute, |
|||
'sceneAbsolute': sceneAbsolute |
|||
}, |
|||
function(data) { |
|||
// Set the values we get back
|
|||
$('#sceneAbsolute_' + showId + '_' + forAbsolute).val( |
|||
(null === data.sceneAbsolute) ? '' : data.sceneAbsolute); |
|||
if (!data.success) |
|||
alert(data.errorMessage ? data.errorMessage : 'Update failed.'); |
|||
} |
|||
); |
|||
} |
|||
|
|||
function qTips(select$){ |
|||
select$.each(function() { |
|||
$(this).qtip({ |
|||
show: {solo:true}, |
|||
position: {viewport:$(window), my:'left center', adjust:{y:-10, x:2}}, |
|||
style: {classes:'qtip-dark qtip-rounded qtip-shadow qtip-maxwidth'} |
|||
}); |
|||
}); |
|||
} |
|||
qTips($('.addQTip')); |
|||
|
|||
function table_init(table$) { |
|||
$('#sbRoot').ajaxEpSearch({'colorRow': true}); |
|||
$('#sbRoot').ajaxEpSubtitlesSearch(); |
|||
|
|||
if ($.SickGear.config.useFuzzy) { |
|||
fuzzyMoment({ |
|||
containerClass: '.airdate', |
|||
dateHasTime: !1, |
|||
dateFormat: $.SickGear.config.dateFormat, |
|||
timeFormat: $.SickGear.config.timeFormat, |
|||
trimZero: $.SickGear.config.fuzzyTrimZero |
|||
}); |
|||
} |
|||
|
|||
table$.each(function (i, obj) { |
|||
$(obj).has('tbody.collapse tr').tablesorter({ |
|||
widgets: ['zebra'], |
|||
selectorHeaders: '> thead tr.tablesorter-headerRow th', |
|||
textExtraction: { |
|||
'.tablesorter-ep-num': function(node) { |
|||
var n = /(\d+)\)?$/img.exec(''+$(node).find('span').text()); return (null == n ? '' : n[1]); }, |
|||
'.tablesorter-ep-scene': function(node) { |
|||
var n = $(node).find('input'); return n.val() || n.attr('placeholder'); }, |
|||
'.tablesorter-airdate': function(node) { return $(node).find('span').attr('data-airdate') || ''; } |
|||
}, |
|||
headers: { |
|||
'.tablesorter-no-sort': {sorter: !1, parser: !1}, |
|||
'.tablesorter-ep-num': {sorter: 'digit'}, |
|||
'.tablesorter-airdate': {sorter: 'digit'} |
|||
} |
|||
}); |
|||
|
|||
$(obj).find('.seasonCheck').on('click', function() { |
|||
var seasCheck = this, seasNo = $(seasCheck).attr('id'); |
|||
|
|||
$(obj).find('.epCheck:visible').each(function() { |
|||
var epParts = $(this).attr('id').split('x'); |
|||
if (epParts[0] == seasNo) |
|||
this.checked = seasCheck.checked |
|||
|
|||
}); |
|||
}); |
|||
|
|||
var lastCheck = null; |
|||
$(obj).find('.epCheck').on('click', function(event) { |
|||
|
|||
if (!lastCheck || !event.shiftKey) { |
|||
lastCheck = this; |
|||
return; |
|||
} |
|||
|
|||
var check = this, found = 0; |
|||
$(obj).find('.epCheck').each(function() { |
|||
switch(found) { |
|||
case 2: |
|||
return !1; |
|||
case 1: |
|||
this.checked = lastCheck.checked; |
|||
} |
|||
(this == check || this == lastCheck) && found++; |
|||
}); |
|||
lastCheck = this; |
|||
}); |
|||
|
|||
qTips($(obj).find('.addQTip')); |
|||
plotter($(obj).find('.plotInfo')); |
|||
|
|||
$(obj).find('.sceneSeasonXEpisode').change(function() { |
|||
// Strip non-numeric characters
|
|||
$(this).val($(this).val().replace(/[^0-9xX]*/g, '')); |
|||
|
|||
var forSeason = $(this).attr('data-for-season'), |
|||
forEpisode = $(this).attr('data-for-episode'), |
|||
m = $(this).val().match(/^(\d+)x(\d+)$/i), |
|||
sceneSeason = m && m[1] || null, sceneEpisode = m && m[2] || null; |
|||
|
|||
setEpisodeSceneNumbering(forSeason, forEpisode, sceneSeason, sceneEpisode); |
|||
}); |
|||
|
|||
$(obj).find('.sceneAbsolute').change(function() { |
|||
// Strip non-numeric characters
|
|||
$(this).val($(this).val().replace(/[^0-9xX]*/g, '')); |
|||
|
|||
var forAbsolute = $(this).attr('data-for-absolute'), |
|||
m = $(this).val().match(/^(\d{1,3})$/i), |
|||
sceneAbsolute = m && m[1] || null; |
|||
|
|||
setAbsoluteSceneNumbering(forAbsolute, sceneAbsolute); |
|||
}); |
|||
}); |
|||
} |
|||
table_init($('.sickbeardTable')); |
|||
|
|||
$.SickGear.season = []; |
|||
$.SickGear.run = !1; |
|||
$('button[id*="showseason-"]').on('click', function() { |
|||
var that = this, this$ = $('#' + this.id), table$ = this$.parents('.sickbeardTable'); |
|||
|
|||
if (0 < table$.find('tbody').find('tr').length) { |
|||
table$.toggleClass('open'); |
|||
} else { |
|||
table$.find('span.images').toggleClass('hide'); |
|||
this$.toggleClass('hide'); |
|||
function fetchSeason() { |
|||
if (0 == $.SickGear.season.length) |
|||
return; |
|||
|
|||
var season = $.SickGear.season[0]; |
|||
$.SickGear.season.shift(); |
|||
$.getJSON($.SickGear.Root + '/home/display_season', {'show': $('#showID').val(), 'season': season}, |
|||
function(data) { |
|||
if (!data.success) { |
|||
alert('Season listing failed.'); |
|||
} else { |
|||
table$.find('tbody').html(data.success); |
|||
table_init(table$); |
|||
} |
|||
table$.toggleClass('open'); |
|||
this$.toggleClass('hide'); |
|||
table$.find('span.images').toggleClass('hide'); |
|||
fetchSeason() |
|||
} |
|||
); |
|||
} |
|||
$.SickGear.season.push(this.id); |
|||
var result = []; |
|||
$.each($.SickGear.season, function(i, e) { |
|||
if (-1 == $.inArray(e, result)) result.push(e); |
|||
}); |
|||
$.SickGear.season = result; |
|||
if (!$.SickGear.run && 1 == $.SickGear.season.length) $.SickGear.run = !0 && fetchSeason(); |
|||
} |
|||
return !1; |
|||
}); |
|||
|
|||
$('button.allseasons').on('click', function() { |
|||
$('table.sickbeardTable:not(.display-season)').each(function() { |
|||
$(this).find('button[id*="showseason-"]').click(); |
|||
}); |
|||
|
|||
var liveStates = $('#display-show'); |
|||
return liveStates.toggleClass('min'), $.get($.SickGear.Root + '/live_panel/?allseasons=' |
|||
+ String.prototype.toLowerCase.apply(+liveStates.hasClass('min'))), !1; |
|||
}); |
|||
|
|||
}); |
|||
|
@ -0,0 +1,296 @@ |
|||
/** @namespace $.SickGear.Root */ |
|||
/** @namespace config.hasArt */ |
|||
/** @namespace config.panelTitles */ |
|||
$(document).ready(function() { |
|||
|
|||
var panel$ = $('#livepanel'), |
|||
pTitle = config.panelTitles || [], |
|||
isEpisodeView = !!$('#episode-view').length, |
|||
liveStates$ = $(isEpisodeView ? '#episode-view' : '#display-show'), |
|||
jqTooltipUsed = /(?!undefined)/i.test(typeof($('body').tooltip)), |
|||
group = 'group', fave = 'fave', avoid = 'avoid', ratingVerbs = [group, fave, avoid].join(' '); |
|||
|
|||
panel$.removeClass('off'); |
|||
|
|||
$('#viewart').on('click', function() { |
|||
var state = 0, on = '', result = !1; |
|||
|
|||
if (isEpisodeView) { |
|||
if (isSet('open-gear')) { |
|||
state = 4; on = 'viewart'; |
|||
} else if (!isSet('viewart')) { |
|||
state = 3; on = 'open-gear'; |
|||
} |
|||
} else if (!isSet('back-art')) { |
|||
if (!isSet('poster-right')) { |
|||
state = 1; on = 'poster-right'; |
|||
} |
|||
} else if (isSet('open-gear')) { |
|||
state = 4; on = 'viewart'; |
|||
} else if (isSet('poster-off')) { |
|||
state = 3; on = 'open-gear'; |
|||
} else if (isSet('poster-right')) { |
|||
state = 2; on = 'poster-off'; |
|||
} else if (!isSet('viewart')) { |
|||
state = 1; on = 'poster-right'; |
|||
} |
|||
liveStates$.removeClass('poster-right poster-off open-gear viewart').addClass(on); |
|||
refreshTitles($(this).attr('id')); |
|||
send('viewart=' + state); |
|||
|
|||
var container = []; |
|||
$.each($('[id^=day]'), function() { container.push($('#' + $(this).attr('id'))) }); |
|||
$.each(container, function() { $(this).isotope('layout') }); |
|||
|
|||
return result; |
|||
}); |
|||
|
|||
$('#back-art,#translucent').on('click', function() { |
|||
var result = !1, |
|||
highlight = panel$.hasClass('highlight-off') || |
|||
panel$.hasClass('highlight2') && panel$.removeClass('highlight2').addClass('highlight1') || |
|||
panel$.hasClass('highlight1') && panel$.removeClass('highlight1').addClass('highlight') || |
|||
panel$.removeClass('highlight').addClass('highlight-off'); |
|||
|
|||
if (config.hasArt) { |
|||
var elid = $(this).attr('id'); |
|||
|
|||
liveStates$.toggleClass(elid); |
|||
refreshTitles(elid); |
|||
send(elid.replace('-', '') + '=' + String.prototype.toLowerCase.apply(+isSet(elid))); |
|||
} |
|||
return result; |
|||
}); |
|||
|
|||
$('#proview').on('click', function() { |
|||
var state = 0, on = 'reg', result = !1; |
|||
|
|||
if (!isEpisodeView && isSet('viewart')) { |
|||
liveStates$.toggleClass('allart'); |
|||
} else { |
|||
if (isSet('reg')) { |
|||
state = 1; on = 'pro'; |
|||
} else if(isSet('back-art') && !isSet('allart')) { |
|||
if (isSet('ii')) { |
|||
state = 3; on = 'pro ii allart'; |
|||
} else if (isSet('pro')) { |
|||
state = 2; on = 'pro ii'; |
|||
} |
|||
} |
|||
liveStates$.removeClass('reg pro ii allart').addClass(on); |
|||
send('viewmode=' + state); |
|||
} |
|||
maybeBackground(); |
|||
refreshTitles($(this).attr('id')); |
|||
return result; |
|||
}); |
|||
|
|||
/* |
|||
* back art related |
|||
*/ |
|||
function maybeArrows() { |
|||
var backArts$ = $('#background-container'), result = !0; |
|||
|
|||
if (isSet('allart') |
|||
|| (!isSet(fave) && |
|||
(1 < backArts$.find('li.' + group).length || |
|||
(1 != backArts$.find('li.' + group).length && 1 < backArts$.find('li').not('.' + group + ',.' + avoid).length))) |
|||
|| (isEpisodeView && |
|||
1 < (backArts$.find('li.' + group).length + backArts$.find('li.' + fave).length + |
|||
backArts$.find('li').not('.' + group + ',.' + avoid).length))) { |
|||
liveStates$.removeClass('oneof'); |
|||
} else { |
|||
liveStates$.addClass('oneof'); |
|||
} |
|||
return result |
|||
} |
|||
|
|||
function setArt(dir) { |
|||
var backArts$ = $('#background-container'), curArt$ = backArts$.find('li.background'), |
|||
faveArt$ = backArts$.find('li.' + fave), result = !0, |
|||
newArt$, init = !1, noArt = function(el) { return /undefined/i.test(typeof(el.css('background-image'))); }, |
|||
viewable = !isSet('allart') && !!backArts$.find('li.' + group).length ? (isEpisodeView ? '': '.' + group) : '', |
|||
mayAvoid = !isSet('allart') ? '.' + avoid : '.showall', |
|||
artBefore$ = curArt$.prevAll(viewable).not(mayAvoid), |
|||
artAfter$ = curArt$.nextAll(viewable).not(mayAvoid); |
|||
|
|||
switch (dir) { |
|||
case 'next': |
|||
if (noArt(newArt$ = artAfter$.first()) && noArt(newArt$ = artBefore$.last()) |
|||
&& noArt(newArt$ = curArt$)) |
|||
newArt$ = null; |
|||
break; |
|||
case 'prev': |
|||
if (noArt(newArt$ = artBefore$.first()) && noArt(newArt$ = artAfter$.last()) |
|||
&& noArt(newArt$ = curArt$)) |
|||
newArt$ = null; |
|||
break; |
|||
case 'init': |
|||
init = !0; |
|||
if (noArt(newArt$ = curArt$)) |
|||
newArt$ = null; |
|||
break; |
|||
case fave: |
|||
newArt$ = faveArt$; |
|||
break; |
|||
} |
|||
|
|||
if (!init || (null == newArt$)) |
|||
curArt$.addClass('background-rem').removeClass('background') |
|||
.fadeOut(800, 'linear', function() {$(this).removeClass('background-rem')}); |
|||
|
|||
if (null !== newArt$) { |
|||
newArt$.addClass('background').fadeIn(800, 'linear', function () { |
|||
$(this).removeClass('first-load') |
|||
}); |
|||
|
|||
liveStates$.removeClass(ratingVerbs).addClass( |
|||
newArt$.hasClass(group) && group || newArt$.hasClass(fave) && fave || newArt$.hasClass(avoid) && avoid || ''); |
|||
} |
|||
|
|||
maybeArrows(); |
|||
refreshTitles(); |
|||
return result; |
|||
} |
|||
setArt('init'); |
|||
|
|||
function maybeBackground() { |
|||
var backArts$ = $('#background-container'), result = !0; |
|||
|
|||
if (isSet('allart')) { |
|||
if (!backArts$.find('li.background').length) { |
|||
backArts$.find('li').first().hide().addClass('background') |
|||
.fadeIn(400, 'linear', function() {$(this).removeClass('first-load')}); |
|||
} |
|||
} else { |
|||
if (backArts$.find('li.' + fave).not('.background').length) { |
|||
setArt(fave); |
|||
} else if (!!backArts$.find('li.' + avoid).length |
|||
&& backArts$.find('li.' + avoid).length == backArts$.find('li').length) { |
|||
backArts$.find('li.' + avoid).fadeOut(800, 'linear', function () { |
|||
$(this).removeClass('background') |
|||
}); |
|||
} else if (backArts$.find('li.background.' + avoid).length) { |
|||
setArt('next'); |
|||
} |
|||
} |
|||
maybeArrows(); |
|||
return result; |
|||
} |
|||
|
|||
$('#art-next,#art-prev').on('click', function() { |
|||
return (!(setArt('art-prev' === $(this).attr('id') ? 'prev' : 'next'))); |
|||
}); |
|||
|
|||
function key(e, kCode){ |
|||
return e.hasOwnProperty('ctrlKey') && e.ctrlKey && e.hasOwnProperty('altKey') && e.altKey && (kCode == e.which) |
|||
} |
|||
$(document).on('keyup', function(e) { |
|||
var left = key(e, 37), up = key(e, 38), right = key(e, 39), down = key(e, 40), |
|||
s = key(e, 83), a = key(e, 65), f = key(e, 70), g = key(e, 71); |
|||
return ( |
|||
(!isSet('oneof') && ((left && setArt('prev')) || (right && setArt('next')))) |
|||
|| (s && liveStates$.toggleClass('allart') && maybeBackground() && refreshTitles('proview')) |
|||
|| (g && setGroup()) || (up && setGroup() && (!isSet('allart') && $('#viewart').click() || !0)) |
|||
|| (a && setAvoid()) || (down && setAvoid() && (!isSet('allart') && $('#translucent').click() || !0)) |
|||
|| (f && setFave()) |
|||
); |
|||
}); |
|||
|
|||
function rate(state, rating) { |
|||
var result = !0; |
|||
|
|||
if (isSet('allart')) { |
|||
var rated = rating && isSet(rating); |
|||
liveStates$.removeClass(ratingVerbs); |
|||
if (rated) { |
|||
state = 0; |
|||
rating = ''; |
|||
} else |
|||
liveStates$.addClass(rating); |
|||
|
|||
var curArt$ = $('#background-container').find('li.background'), |
|||
art = /\?([^"]+)"/i.exec(curArt$.css('background-image')); |
|||
if (null != art) { |
|||
send('rate=' + state + '&' + art[1]); |
|||
curArt$.removeClass().addClass((!!rating.length ? rating + ' ' : '') + 'background'); |
|||
} |
|||
maybeBackground(); |
|||
refreshTitles('rate-art'); |
|||
} |
|||
return result; |
|||
} |
|||
function setAvoid() {return rate(30, avoid);} |
|||
function setFave() {return rate(20, fave);} |
|||
function setGroup() {return rate(10, group);} |
|||
function setRnd() {return rate(0, '');} |
|||
$('#rate-art').on('click', function() { |
|||
return isSet('allart') && |
|||
((isSet(fave) && setAvoid()) || (isSet(group) && setFave()) || (!isSet(avoid) && setGroup()) || setRnd()) || !0; |
|||
}); |
|||
|
|||
/* |
|||
* support functions |
|||
*/ |
|||
function isSet(name) {return liveStates$.hasClass(name)} |
|||
|
|||
function send(value) { |
|||
return $.get($.SickGear.Root + '/live_panel/?' + value + '&pg=' + (isEpisodeView ? 'ev' : 'ds'))} |
|||
|
|||
if (jqTooltipUsed) { |
|||
panel$.find('a[title]').tooltip({placement: 'top', html: !0}); |
|||
} |
|||
|
|||
function refreshTitle(target$, title, refreshAll) { |
|||
return jqTooltipUsed |
|||
? target$.attr('data-original-title', title.replace(/<[\/]?em>/g, '')).tooltip('fixTitle') && refreshAll //|| target$.tooltip('show')
|
|||
: target$.attr('title', title); |
|||
} |
|||
|
|||
function refreshTitles(id) { |
|||
if (!$('#livepanel').length) return; |
|||
|
|||
var refreshAll = /undefined/i.test(typeof(id)), elId = !refreshAll && id.replace('#', '') || id, result = !0; |
|||
if ('viewart' === elId || refreshAll) { |
|||
refreshTitle($('#viewart'), |
|||
isSet('poster-right') ? pTitle['viewart1'] |
|||
: (isSet('back-art') ? |
|||
(isSet('viewart') ? pTitle['viewart4'] |
|||
: (isSet('open-gear') ? pTitle['viewart3'] |
|||
: (isSet('poster-off') ? pTitle['viewart2'] |
|||
: (isEpisodeView ? pTitle['viewmode0'] : pTitle['viewart0'])))) |
|||
: (isEpisodeView ? pTitle['viewmode0'] : pTitle['viewart0'])), |
|||
refreshAll); |
|||
} |
|||
if ('translucent' === elId || refreshAll) { |
|||
refreshTitle($('#translucent'), isSet('translucent') ? pTitle['translucent_on'] : pTitle['translucent_off'], |
|||
refreshAll); |
|||
} |
|||
if (config.hasArt && ('back-art' === elId || refreshAll)) { |
|||
refreshTitle($('#back-art'), isSet('back-art') ? pTitle['backart_on'] : pTitle['backart_off'], |
|||
refreshAll); |
|||
} |
|||
if ('rate-art' === elId || refreshAll) { |
|||
refreshTitle($('#rate-art'), |
|||
isSet(avoid) ? pTitle['rateart3'] |
|||
: (isSet(fave) ? pTitle['rateart2'] |
|||
: (isSet(group) ? pTitle['rateart1'] |
|||
: pTitle['rateart0'])), |
|||
refreshAll); |
|||
} |
|||
if ('proview' === elId || refreshAll) { |
|||
refreshTitle($('#proview'), |
|||
isSet('back-art') ? |
|||
(isSet('allart') ? pTitle['viewmode3'] |
|||
: (isSet('ii') ? pTitle['viewmode2'] |
|||
: (isSet('pro') ? pTitle['viewmode1'] |
|||
: pTitle['viewmode0']))) |
|||
: (isSet('pro') ? pTitle['viewmode1'] |
|||
: pTitle['viewmode0']), |
|||
refreshAll); |
|||
} |
|||
return result; |
|||
} |
|||
refreshTitles(); |
|||
|
|||
}); |
@ -1,46 +1,47 @@ |
|||
$(function () { |
|||
$('.plotInfo, .plot-daybyday').each(function () { |
|||
var match = $(this).attr('id').match(/^plot(?:_info_|-)((\d+)_(\d+)[_x](\d+))$/); |
|||
var showName = $('#show-' + match[1]).attr('data-rawname'); |
|||
$(this).qtip({ |
|||
content: { |
|||
text: function(event, api) { |
|||
// deferred object ensuring the request is only made once
|
|||
$.ajax({ |
|||
url: $('#sbRoot').val() + '/home/plotDetails', |
|||
type: 'GET', |
|||
data: { |
|||
show: match[2], |
|||
episode: match[4], |
|||
season: match[3] |
|||
} |
|||
}) |
|||
.then(function(content) { |
|||
// Set the tooltip content upon successful retrieval
|
|||
api.set('content.text', ('undefined' === typeof(showName) ? '' |
|||
: ('' !== content ? '<b class="boldest">' + showName + '</b>' : showName)) |
|||
+ ('' !== content ? ' ' + content : '')); |
|||
}, function(xhr, status, error) { |
|||
// Upon failure... set the tooltip content to the status and error value
|
|||
api.set('content.text', status + ': ' + error); |
|||
}); |
|||
return 'Loading...'; // Set initial text
|
|||
} |
|||
}, |
|||
show: { |
|||
solo: true |
|||
}, |
|||
position: { |
|||
viewport: $(window), |
|||
my: 'left center', |
|||
adjust: { |
|||
y: -10, |
|||
x: 0 |
|||
} |
|||
}, |
|||
style: { |
|||
classes: 'qtip-rounded qtip-shadow' |
|||
} |
|||
}); |
|||
}); |
|||
}); |
|||
var plotter = function(select$) { |
|||
select$.each(function() { |
|||
var match = $(this).attr('id').match(/^plot(?:_info_|-)((\d+)_(\d+)[_x](\d+))$/); |
|||
var showName = $('#show-' + match[1]).attr('data-rawname'); |
|||
$(this).qtip({ |
|||
content: { |
|||
text: function(event, api) { |
|||
// deferred object ensuring the request is only made once
|
|||
$.ajax({ |
|||
url: $.SickGear.Root + '/home/plotDetails', |
|||
type: 'GET', |
|||
data: { |
|||
show: match[2], |
|||
episode: match[4], |
|||
season: match[3] |
|||
} |
|||
}) |
|||
.then(function(content) { |
|||
// Set the tooltip content upon successful retrieval
|
|||
api.set('content.text', ('undefined' === typeof(showName) ? '' |
|||
: ('' !== content ? '<b class="boldest">' + showName + '</b>' : showName)) |
|||
+ ('' !== content ? ' ' + content : '')); |
|||
}, function(xhr, status, error) { |
|||
// Upon failure... set the tooltip content to the status and error value
|
|||
api.set('content.text', status + ': ' + error); |
|||
}); |
|||
return 'Loading...'; // Set initial text
|
|||
} |
|||
}, |
|||
show: { |
|||
solo: true |
|||
}, |
|||
position: { |
|||
viewport: $(window), |
|||
my: 'left center', |
|||
adjust: { |
|||
y: -10, |
|||
x: 0 |
|||
} |
|||
}, |
|||
style: { |
|||
classes: 'qtip-dark qtip-rounded qtip-shadow' |
|||
} |
|||
}); |
|||
}); |
|||
}; |
|||
$(function () { plotter($('.plotInfo, .plot-daybyday')) }); |
|||
|
@ -0,0 +1,98 @@ |
|||
__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): |
|||
ART = 'clearart' |
|||
LOGO = 'clearlogo' |
|||
CHARACTER = 'characterart' |
|||
THUMB = 'tvthumb' |
|||
SEASONTHUMB = 'seasonthumb' |
|||
BACKGROUND = 'showbackground' |
|||
HDLOGO = 'hdtvlogo' |
|||
HDART = 'hdclearart' |
|||
POSTER = 'tvposter' |
|||
BANNER = 'tvbanner' |
|||
|
|||
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' |
|||
|
|||
|
|||
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] |
@ -0,0 +1,86 @@ |
|||
import requests |
|||
import re |
|||
import lib.fanart as fanart |
|||
from sickbeard.bs4_parser import BS4Parser |
|||
from .errors import ResponseFanartError |
|||
|
|||
|
|||
class Request(object): |
|||
def __init__(self, apikey, tvdb_id, ws=fanart.WS.TV): |
|||
self._apikey = apikey |
|||
self._tvdb_id = tvdb_id |
|||
self._ws = ws |
|||
self._response = None |
|||
self._web_url = 'https://fanart.tv/series/%s' |
|||
self._assets_url = 'https://assets.fanart.tv' |
|||
|
|||
def __str__(self): |
|||
return fanart.BASEURL % (self._ws, self._tvdb_id, self._apikey) |
|||
|
|||
def response(self): |
|||
|
|||
try: |
|||
response = requests.get(str(self)) |
|||
rjson = response.json() |
|||
image_type = u'showbackground' |
|||
rhtml = self.scrape_web(image_type) |
|||
if not isinstance(rjson, dict) and 0 == len(rhtml[image_type]): |
|||
raise Exception(response.text) |
|||
|
|||
if 0 < len(rhtml[image_type]): |
|||
items = {image_type: []} |
|||
for item1 in rhtml[image_type]: |
|||
use_item = True |
|||
for k, item2 in enumerate(rjson[image_type] or []): |
|||
if '00' == item2['lang']: # adjust api data of no language to a default |
|||
rjson[image_type][k]['lang'] = u'en' |
|||
if item1['id'] == item2['id']: |
|||
use_item = False |
|||
break |
|||
if use_item: |
|||
items[image_type] += [item1] |
|||
rjson[image_type] += items[image_type] |
|||
return rjson |
|||
|
|||
except Exception as e: |
|||
raise ResponseFanartError(str(e)) |
|||
|
|||
def scrape_web(self, image_type): |
|||
try: |
|||
data = requests.get(self._web_url % self._tvdb_id) |
|||
if not data: |
|||
return |
|||
|
|||
with BS4Parser(data.text, features=['html5lib', 'permissive']) as html: |
|||
ul_item = html.find('ul', attrs={'class': image_type}) |
|||
if ul_item: |
|||
li_items = ul_item('li') |
|||
if li_items: |
|||
image_urls = {image_type: []} |
|||
for li_item in li_items: |
|||
image_id = None |
|||
item = li_item.find('a', attrs={'class': 'download'}).get('href') |
|||
if item: |
|||
match = re.search(r'image=(\d+)', item, re.I) |
|||
if match: |
|||
image_id = u'%s' % match.group(1) |
|||
|
|||
item = li_item.find('a', attrs={'rel': image_type}).get('href') |
|||
image_url = (u'%s%s' % (self._assets_url, item), None)[None is item] |
|||
|
|||
item = li_item.find('div', attrs={'class': 'votes'}).get_text() |
|||
image_likes = (item, 0)[None is item] |
|||
|
|||
item = li_item.find('div', attrs={'class': 'metrics'}).get_text() |
|||
image_lang = u'None found' |
|||
if item: |
|||
match = re.search(r'Language:\s*(\w+)', item, re.I) |
|||
if match: |
|||
image_lang = u'%s' % (match.group(1)[0:2:].lower(), 'en')['None' == match.group(1)] |
|||
|
|||
if not (None is image_id or None is image_url): |
|||
image_urls[image_type].append({u'id': image_id, u'url': image_url, u'likes': image_likes, u'lang': image_lang}) |
|||
|
|||
return image_urls |
|||
except Exception, e: |
|||
pass |
@ -0,0 +1,11 @@ |
|||
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 |
@ -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 |
@ -0,0 +1,68 @@ |
|||
import json |
|||
import os |
|||
import requests |
|||
from .core import Request |
|||
from .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'), |
|||
tvdb_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()] |
@ -0,0 +1,108 @@ |
|||
import lib.fanart as fanart |
|||
from .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), |
|||
) |