Blob download_and_prepare_library.py
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
#!/usr/bin/env python
# coding:utf-8
import sys
import pyjsonrpc
import os
import requests
import multiprocessing
import logging
import json
import click
from urllib import unquote
from glob import glob
from getpass import getpass
BASEDIR = os.path.dirname(os.path.realpath(__file__))
THUMBNAIL_CACHE = os.path.join(BASEDIR, ".cache/thumbnails")
THUMBNAIL_DIR = os.path.join(BASEDIR, "app/img/thumbs")
logging.basicConfig()
logger = logging.getLogger(__name__)
@click.command()
@click.option('--host', help='XBMC server address', prompt=True, default='192.168.178.25')
@click.option('--username', help='Username for logging in on XBMC', prompt=True)
@click.option('--password', help='Password for logging in on XBMC', prompt=True, hide_input=True)
@click.option('--processes', help='Number of processes to use during download/conversion', type=click.INT, default=1)
@click.option('--output', help='Name of the file to output the library as JSON', default='data.js')
@click.option('--pretty', help='Prettify the JSON output', is_flag=True)
@click.option('--debug', help='Enable debugging messages', is_flag=True)
def update_library(host, username, password, processes, output, pretty, debug):
if debug:
logger.setLevel(logging.DEBUG)
jsonrpc_endpoint = "http://{}/jsonrpc".format(host)
logger.debug("Connecting to {} with {}:{}".format(jsonrpc_endpoint,
username, password))
global jsonclient
jsonclient = pyjsonrpc.HttpClient(url=jsonrpc_endpoint,
username=username,
password=password)
response = jsonclient.call('VideoLibrary.GetMovies',
properties = [
"director", "title", "genre",
"dateadded", "playcount", "originaltitle",
"thumbnail", "year", "imdbnumber",
"lastplayed", "plot",
],
sort = dict(order="descending", method="dateadded")
)
logger.debug("Retrieved list of movies")
movies = response['movies']
if not os.path.isdir(THUMBNAIL_CACHE):
logger.info("Thumbnail cache dir doesn't exist. Creating it...")
os.makedirs(THUMBNAIL_CACHE)
logger.info("Found {} movies to process. Using up to {} processes".format(
len(movies), processes
))
pool = multiprocessing.Pool(processes)
logger.info("Downloading metadata")
pool.map(download_original_thumbnail, movies)
logger.info("Resizing thumbnails")
pool.map(resize_thumbnail, movies)
logger.info("Generating json output")
write_json_data(movies, output, pretty)
logger.info("Done")
def download_original_thumbnail(movie):
global jsonclient
movie_id = movie['imdbnumber']
logger.debug("Processing movie id {}".format(movie['movieid']))
basepath = os.path.join(THUMBNAIL_CACHE, str(movie_id))
existing = glob("{}.*".format(basepath))
if len(existing) > 0:
# file exists, nothing to do
logger.debug("Thumb for {} already downloaded as {}".format(movie_id, existing[0]))
return
thumb_url = unquote(movie['thumbnail'][8:-1])
request_args = {}
result = jsonclient.call('Files.PrepareDownload', movie['thumbnail'])
if result['mode'] == 'redirect':
logger.debug("{}: Using VFS instead of direct download".format(movie_id))
(_, vfs_path) = result['details']['path'].split("/", 1)
if jsonclient.username is not None and jsonclient.password is not None:
request_args['auth'] = (jsonclient.username, jsonclient.password)
vfs_url = jsonclient.url.replace('jsonrpc', 'vfs')
thumb_url = '{}/{}'.format(vfs_url, vfs_path)
logger.debug("URL being used to download: {}".format(thumb_url))
req = requests.get(thumb_url, stream=True, **request_args)
# FIXME I'm assuming jpg is always the proper format
extension = 'jpg'
filename = "{}.{}".format(basepath, extension)
with open(filename, 'wb') as fd:
for chunk in req.iter_content(1000):
fd.write(chunk)
logger.debug("Successfully downloaded {}".format(filename))
return
def write_json_data(movies, outputfile, pretty=False):
output = []
for movie in movies:
jsondata = dict(
id=movie['movieid'],
title=movie['title'],
year=movie['year'],
imdbnumber=movie['imdbnumber'],
genres=movie.get('genre', []),
directors=movie.get('director', []),
plot=movie['plot'],
playcount=movie['playcount'],
lastplayed=movie['lastplayed'],
)
output.append(jsondata)
kwargs = {}
if pretty:
kwargs = dict(sort_keys=True, indent=2, separators=(',', ': '))
with open(outputfile, 'wb') as fd:
fd.write('var movies = ' + json.dumps(output, **kwargs) + ';')
def resize_thumbnail(movie):
movie_id = movie['imdbnumber']
original_thumbnail = os.path.join(THUMBNAIL_CACHE, "{}.jpg".format(movie_id))
final_thumbnail = os.path.join(THUMBNAIL_DIR, "{}.jpg".format(movie_id))
if os.path.exists(final_thumbnail):
logger.debug("Resized thumbnail already exists for movie {}".format(movie_id))
else:
logger.info("Generating {}".format(final_thumbnail))
os.system("convert {} -resize '200x300!' -strip {}".format(original_thumbnail, final_thumbnail))
if __name__ == '__main__':
# FIXME I use jsonclient as a global here because partial() and
# multiprocessing weren't playing nicelly on Python 2.x. On 3x
# it should work since `partial` becomes pickle-able.
jsonclient = None
update_library()
|