Skip to content

Commit

Permalink
V 1.2.0
Browse files Browse the repository at this point in the history
Best works with Shoko 3.8 beta or newer.
  • Loading branch information
Cazzar authored Aug 20, 2017
2 parents 790a8e8 + 9a924e2 commit 11511db
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 82 deletions.
66 changes: 47 additions & 19 deletions Contents/Code/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import os, re, time, string, thread, threading, urllib, copy
from lxml import etree
import os
import re
import time
import string
import thread
import threading
import urllib
import copy
from datetime import datetime
from lxml import etree
import tags as TagBlacklist

API_KEY = ''
PLEX_HOST = ''

#this is from https://github.com/plexinc-agents/PlexThemeMusic.bundle/blob/master/Contents/Code/__init__.py
THEME_URL = 'http://tvthemes.plexapp.com/%s.mp3'


def ValidatePrefs():
pass
Expand All @@ -14,6 +24,7 @@ def ValidatePrefs():
def Start():
Log("Shoko metata agent started")
HTTP.Headers['Accept'] = 'application/json'
HTTP.CacheTime = 0 #cache, can you please go away, typically we will be requesting LOCALLY. HTTP.CacheTime
ValidatePrefs()


Expand Down Expand Up @@ -71,7 +82,7 @@ def Update(self, metadata, media, lang, force, movie):
# http://127.0.0.1:8111/api/ep/getbyfilename?apikey=d422dfd2-bdc3-4219-b3bb-08b85aa65579&filename=%5Bjoseole99%5D%20Clannad%20-%2001%20(1280x720%20Blu-ray%20H264)%20%5B8E128DF5%5D.mkv

# episode_data = HttpReq("api/ep/getbyfilename?apikey=%s&filename=%s" % (GetApiKey(), urllib.quote(media.filename)))
series = HttpReq("api/serie?id=%s&level=3" % aid)
series = HttpReq("api/serie?id=%s&level=3&allpics=1" % aid)

# build metadata on the TV show.
metadata.summary = try_get(series, 'summary')
Expand All @@ -93,17 +104,9 @@ def Update(self, metadata, media, lang, force, movie):

metadata.genres = tags

if len(series['art']['banner']):
for art in series['art']['banner']:
metadata.banner[art['url']] = Proxy.Media(HTTP.Request(art['url']).content, art['index'])

if len(series['art']['thumb']):
for art in series['art']['thumb']:
metadata.posters[art['url']] = Proxy.Media(HTTP.Request(art['url']).content, art['index'])

if len(series['art']['fanart']):
for art in series['art']['fanart']:
metadata.art[art['url']] = Proxy.Media(HTTP.Request(art['url']).content, art['index'])
self.metadata_add(metadata.banners, series['art']['banner'])
self.metadata_add(metadata.posters, series['art']['thumb'])
self.metadata_add(metadata.art, series['art']['fanart'])

### Generate general content ratings.
### VERY rough approximation to: https://www.healthychildren.org/English/family-life/Media/Pages/TV-Ratings-A-Guide-for-Parents.aspx
Expand Down Expand Up @@ -135,7 +138,7 @@ def Update(self, metadata, media, lang, force, movie):

if not movie:
for ep in series['eps']:
if ep['eptype'] != 1:
if ep['eptype'] != "Episode":
continue

season = 1
Expand All @@ -148,13 +151,38 @@ def Update(self, metadata, media, lang, force, movie):
episodeObj.title = ep['name']
episodeObj.summary = ep['summary']

if ep['air'] != '1/01/0001 12:00:00 AM':
if ep['air'] != '1/01/0001 12:00:00 AM' and ep['air'] != '0001-01-01':
episodeObj.originally_available_at = datetime.strptime(ep['air'], "%d/%m/%Y %H:%M:%S %p").date()

if len(series['art']['thumb']):
if len(series['art']['thumb']) and Prefs['customThumbs']:
for art in series['art']['thumb']:
episodeObj.thumbs[art['url']] = Proxy.Media(HTTP.Request(art['url']).content, art['index'])

episodeObj.thumbs[art['url']] = Proxy.Media(HTTP.Request("http://{host}:{port}{relativeURL}".format(host=Prefs['Hostname'], port=Prefs['Port'], relativeURL=art['url'])).content, art['index'])

links = HttpReq("api/links/serie?id=%s" % aid)

#adapted from: https://github.com/plexinc-agents/PlexThemeMusic.bundle/blob/fb5c77a60c925dcfd60e75a945244e07ee009e7c/Contents/Code/__init__.py#L41-L45
if Prefs["themeMusic"]:
for tid in links["tvdb"]:
if THEME_URL % tid not in metadata.themes:
try:
metadata.themes[THEME_URL % tid] = Proxy.Media(HTTP.Request(THEME_URL % tid))
Log("added: %s" % THEME_URL % tid)
except:
Log("error adding music, probably not found")

def metadata_add(self, meta, images):
valid = list()

for art in images:
Log("[metadata_add] :: Adding metadata %s (index %d)" % (art['url'], art['index']))
meta[art['url']] = Proxy.Media(HTTP.Request("http://{host}:{port}{relativeURL}".format(host=Prefs['Hostname'], port=Prefs['Port'], relativeURL=art['url'])).content, art['index'])
valid.append(art['url'])

meta.validate_keys(valid)

for key in meta.keys():
if (key not in valid):
del meta[key]

def try_get(arr, idx, default=""):
try:
Expand Down
16 changes: 15 additions & 1 deletion Contents/DefaultPrefs.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,19 @@
"label": "Hide any miscellaneous tags.",
"type": "bool",
"default": true
},

{
"id": "customThumbs",
"label": "Use shoko's thumbnails",
"type": "bool",
"default": false
},

{
"id": "themeMusic",
"label": "Grab theme music the same way plex theme music would",
"type": "bool",
"default": true
}
]
]
142 changes: 82 additions & 60 deletions Contents/Resources/Series/Shoko Series Scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,42 @@

API_KEY = ''

def log(methodName, message, *args):
'''
Create a log message given the message and arguments
'''
logMsg = message
# Replace the arguments in the string
if args:
logMsg = message % args

logMsg = methodName + ' :: ' + logMsg
print logMsg
### Log + LOG_PATH calculated once for all calls ###
import logging, logging.handlers #
RootLogger = logging.getLogger('main')
RootHandler = None
RootFormatting = logging.Formatter('%(message)s') #%(asctime)-15s %(levelname)s -
RootLogger.setLevel(logging.DEBUG)
Log = RootLogger

FileListLogger = logging.getLogger('FileListLogger')
FileListHandler = None
FileListFormatting = logging.Formatter('%(message)s')
FileListLogger.setLevel(logging.DEBUG)
LogFileList = FileListLogger.info

def set_logging(instance, filename):
global RootLogger, RootHandler, RootFormatting, FileListLogger, FileListHandler, FileListFormatting
logger, handler, formatting, backup_count = [RootLogger, RootHandler, RootFormatting, 9] if instance=="Root" else [FileListLogger, FileListHandler, FileListFormatting, 1]
if handler: logger.removeHandler(handler)
handler = logging.handlers.RotatingFileHandler(os.path.join(LOG_PATH, filename), maxBytes=10*1024*1024, backupCount=backup_count) #handler = logging.FileHandler(os.path.join(LOG_PATH, filename), mode)
#handler.setFormatter(formatting)
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
if instance=="Root": RootHandler = handler
else: FileListHandler = handler

### Check config files on boot up then create library variables ### #platform = xxx if callable(getattr(sys,'platform')) else ""
import inspect
LOG_PATH = os.path.abspath(os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), "..", "..", "Logs"))
if not os.path.isdir(LOG_PATH):
path_location = { 'Windows': '%LOCALAPPDATA%\\Plex Media Server',
'MacOSX': '$HOME/Library/Application Support/Plex Media Server',
'Linux': '$PLEX_HOME/Library/Application Support/Plex Media Server' }
try: path = os.path.expandvars(path_location[Platform.OS.lower()] if Platform.OS.lower() in path_location else '~') # Platform.OS: Windows, MacOSX, or Linux
except: pass #os.makedirs(LOG_PATH) # User folder on MacOS-X
LOG_FILE_LIBRARY = LOG_FILE = 'Shoko Metadata Scanner.log' # Log filename library will include the library name, LOG_FILE not and serve as reference
set_logging("Root", LOG_FILE_LIBRARY)


def HttpPost(url, postdata):
Expand All @@ -34,7 +59,7 @@ def HttpPost(url, postdata):


def HttpReq(url, authenticate=True):
log('HttpReq' ,"Requesting: %s", url)
Log.info("Requesting: %s", url)
api_string = ''
if authenticate:
api_string = '&apikey=%s' % GetApiKey()
Expand All @@ -52,69 +77,66 @@ def GetApiKey():
data = '{"user":"%s", "pass":"%s", "device":"%s"}' % (
Prefs['Username'], Prefs['Password'] if Prefs['Password'] != None else '', 'Shoko Series Scanner For Plex')
resp = HttpPost('api/auth', data)['apikey']
log('GetApiKey', "Got API KEY: %s", resp)
Log.info( "Got API KEY: %s", resp)
API_KEY = resp
return resp

return API_KEY


def Scan(path, files, mediaList, subdirs, language=None, root=None):
log('Scan', 'path: %s', path)
log('Scan', 'files: %s', files)
log('Scan', 'mediaList: %s', mediaList)
log('Scan', 'subdirs: %s', subdirs)
log('Scan', 'language: %s', language)
log('Scan', 'root: %s', root)

# Scan for video files.
VideoFiles.Scan(path, files, mediaList, subdirs, root)

for idx, file in enumerate(files):
log('Scan', 'file: %s', file)
# http://127.0.0.1:8111/api/ep/getbyfilename?apikey=d422dfd2-bdc3-4219-b3bb-08b85aa65579&filename=%5Bjoseole99%5D%20Clannad%20-%2001%20(1280x720%20Blu-ray%20H264)%20%5B8E128DF5%5D.mkv

episode_data = HttpReq("api/ep/getbyfilename?filename=%s" % (urllib.quote(os.path.basename(file))))
if len(episode_data) == 0: break
if (try_get(episode_data, "code", 200) == 404): break

series_data = HttpReq("api/serie/fromep?id=%d&nocast=1&notag=1" % episode_data['id'])
showTitle = series_data['name'].encode("utf-8") #no idea why I need to do this.
log('Scan', 'show title: %s', showTitle)
try:
Log.debug('path: %s', path)
Log.debug('files: %s', files)
Log.debug('mediaList: %s', mediaList)
Log.debug('subdirs: %s', subdirs)
Log.debug('language: %s', language)
Log.info('root: %s', root)

# Scan for video files.
VideoFiles.Scan(path, files, mediaList, subdirs, root)

for idx, file in enumerate(files):
Log.info('file: %s', file)
# http://127.0.0.1:8111/api/ep/getbyfilename?apikey=d422dfd2-bdc3-4219-b3bb-08b85aa65579&filename=%5Bjoseole99%5D%20Clannad%20-%2001%20(1280x720%20Blu-ray%20H264)%20%5B8E128DF5%5D.mkv

seasonNumber = 0
seasonStr = try_get(episode_data, 'season', None)
if seasonStr == None:
if episode_data['eptype'] == 'Episode': seasonNumber = 1
if episode_data['eptype'] == 'Credits': seasonNumber = -1 #season -1 for OP/ED
else:
seasonNumber = seasonStr.split('x')[0]
episode_data = HttpReq("api/ep/getbyfilename?filename=%s" % (urllib.quote(os.path.basename(file))))
if len(episode_data) == 0: break
if (try_get(episode_data, "code", 200) == 404): break

if seasonNumber <= 0 and Prefs['IncludeOther'] == False: break #Ignore this by choice.

series_data = HttpReq("api/serie/fromep?id=%d&nocast=1&notag=1" % episode_data['id'])
showTitle = series_data['name'].encode("utf-8") #no idea why I need to do this.
Log.info('show title: %s', showTitle)

log('Scan', 'season number: %s', seasonNumber)
seasonNumber = 0
seasonStr = try_get(episode_data, 'season', None)
if seasonStr == None:
if episode_data['eptype'] == 'Episode': seasonNumber = 1
if episode_data['eptype'] == 'Credits': seasonNumber = -1 #season -1 for OP/ED
else:
seasonNumber = seasonStr.split('x')[0]

seasonYear = episode_data['year']
log('Scan', 'season year: %s', seasonYear)
if seasonNumber <= 0 and Prefs['IncludeOther'] == False: break #Ignore this by choice.

episodeNumber = int(episode_data['epnumber'])
if episode_data['eptype'] != 'Episode':
episodeNumber = str("%s%d" % (episode_data['eptype'][0], episode_data['epnumber']))
Log.info('season number: %s', seasonNumber)

log('Scan', 'episode number: %s', episodeNumber)
episodeNumber = int(episode_data['epnumber'])
if episode_data['eptype'] != 'Episode':
episodeNumber = str("%s%d" % (episode_data['eptype'][0], episode_data['epnumber']))

episodeTitle = episode_data['name'].encode("utf-8")
log('Scan', 'episode title: %s', episodeTitle)
Log.info('episode number: %s', episodeNumber)

vid = Media.Episode(showTitle, int(seasonNumber), episodeNumber , episodeTitle, int(seasonYear))
log('Scan', 'vid: %s', vid)
vid.parts.append(file)
mediaList.append(vid)

log('Scan', 'stack media')
Stack.Scan(path, files, mediaList, subdirs)
log('Scan', 'media list %s', mediaList)
vid = Media.Episode(showTitle, int(seasonNumber), episodeNumber)
Log.info('vid: %s', vid)
vid.parts.append(file)
mediaList.append(vid)

Log.info('stack media')
Stack.Scan(path, files, mediaList, subdirs)
Log.debug('media list %s', mediaList)
except Exception as e:
Log.error("Error in Scan: '%s'" % e)


def try_get(arr, idx, default=""):
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ Naming episodes/series works best with [This format](https://support.plex.tv/hc/

# Plans

I do plan to in the long term to add things such as scrobbling to this plugin
~~I do plan to in the long term to add things such as scrobbling to this plugin~~ - This has been added to Shoko, via webhooks

Another future plan is in regards to syncing watched status between shoko and plex.
~~Another future plan is in regards to syncing watched status between shoko and plex.~~ - This can be done inside shoko. via webhook or manual plex linking.

0 comments on commit 11511db

Please sign in to comment.