|
#!/usr/bin/env python3 |
|
|
|
import multiprocessing as mp |
|
import argparse |
|
import requests |
|
import logging |
|
import shutil |
|
import os |
|
|
|
extensions = [ |
|
'avi', 'AVI', |
|
'mkv', 'MKV', |
|
'mp4', 'MP4', |
|
'mpg', 'MPG' |
|
] |
|
|
|
class EmbyThumbnails: |
|
def __init__(self, url, token): |
|
self.url = url |
|
self.token = token |
|
self.items = {} |
|
|
|
def __get_extension(self, size): |
|
size = str(size) |
|
extension = int(size[:-3]) |
|
units = int(size[-3:]) |
|
while extension > 4294967 or extension == 4294967 and units >= 296: |
|
extension -= 4294967 |
|
if units >= 296: |
|
units -= 296 |
|
else: |
|
units = 1000 + units - 296 |
|
extension -= 1 |
|
return extension |
|
|
|
def __download_cover(self, id, origFile, size): |
|
filename = '%s.%s' % ( |
|
'.'.join(origFile.split('.')[:-1]), self.__get_extension(size)) |
|
os.makedirs(os.path.dirname(filename), exist_ok=True) |
|
with open(filename, 'wb') as f: |
|
resp = requests.get( |
|
'%s/emby/Items/%s/Images/Primary?MaxWidth=200&MaxHeight=300&Format=jpg' % (self.url, id)) |
|
f.write(resp.content) |
|
os.chmod(filename, 0o644) |
|
logging.info('donwloaded [%s]', filename) |
|
|
|
def load_metadata(self): |
|
libraryPaths = [] |
|
for libraryPath in requests.get('%s/emby/Library/PhysicalPaths?api_key=%s' % (self.url, self.token)).json(): |
|
path = '/'.join(libraryPath.split('/')[:-1]) |
|
if path not in libraryPaths: |
|
libraryPaths.append(path) |
|
|
|
self.items = {} |
|
for library in requests.get('%s/emby/Items?api_key=%s' % (self.url, self.token)).json()['Items']: |
|
for item in requests.get( |
|
'%s/emby/Items?Recursive=true&ParentId=%s&Fields=Path&api_key=%s' % |
|
(self.url, library['Id'], self.token)).json()['Items']: |
|
if 'Path' not in item: |
|
continue |
|
|
|
itemType = item['Type'] |
|
if itemType not in ['Movie', 'Episode']: |
|
continue |
|
|
|
for libraryPath in libraryPaths: |
|
if item['Path'].startswith(libraryPath): |
|
path = item['Path'][len(libraryPath):] |
|
self.items[path] = item['Id' if itemType == |
|
'Movie' else 'SeriesId'] |
|
break |
|
logging.info('loaded [%d] entries', len(self.items)) |
|
|
|
def process_file(self, paths): |
|
self.__download_cover( |
|
self.items[paths[1]], paths[2] + paths[1], os.path.getsize(paths[0])) |
|
|
|
def generate(self, src, dst): |
|
if src.endswith('/'): |
|
src = src[:-1] |
|
if dst.endswith('/'): |
|
dst = dst[:-1] |
|
|
|
shutil.rmtree(dst) |
|
|
|
files = [] |
|
for r, d, f in os.walk(src): |
|
for file in f: |
|
path = os.path.join(r, file) |
|
relPath = path[len(src):] |
|
if file.split('.')[-1] in extensions and relPath in self.items: |
|
files.append((path, relPath, dst)) |
|
logging.info('detected [%s] files', len(files)) |
|
|
|
with mp.Pool(processes=mp.cpu_count()) as p: |
|
results = p.map(self.process_file, files) |
|
|
|
|
|
if __name__ == '__main__': |
|
parser = argparse.ArgumentParser() |
|
optional = parser._action_groups.pop() |
|
|
|
required = parser.add_argument_group('required arguments') |
|
required.add_argument('-u', '--url', required=True) |
|
required.add_argument("-t", "--token", required=True) |
|
required.add_argument("-s", "--src-dir", required=True) |
|
required.add_argument("-d", "--dst-dir", required=True) |
|
|
|
parser._action_groups.append(optional) |
|
args = parser.parse_args() |
|
|
|
logging.basicConfig(format='%(asctime)s | %(levelname)s | %(message)s', |
|
datefmt='%m/%d/%Y %I:%M:%S %p', level=logging.INFO) |
|
|
|
thumbnails = EmbyThumbnails(args.url, args.token) |
|
thumbnails.load_metadata() |
|
thumbnails.generate(args.src_dir, args.dst_dir) |