Jump to content

Python Script to get Local Trailers


llygoden
 Share

Recommended Posts

llygoden

I wanted to populate my library with a local trailer for each film. I was also interested in learning a bit of Python.

 

So this resulted in the script below, if you want to run it you'll need to install Requests and PyTube (can be done using PIP):

import sys
import requests
import urllib
import json
import ConfigParser
import glob
import os
from pytube import YouTube

config = ConfigParser.RawConfigParser()
config.read('trailers.ini')

embyapi = ""
embykey = ""
embyusr = ""

h = 0
i = 0
j = 0
k = 0

if config.has_option('emby', 'api'):
	embyapi = config.get('emby', 'api')
else:
	embyerror = "No Emby URL in Config File.\n"

if config.has_option('emby', 'key'):
	embykey = config.get('emby', 'key')
else:
	embyerror += "No Emby API Key in Config File.\n"

if config.has_option('emby', 'usr'):
	embyusr = config.get('emby', 'usr')
else:
	embyerror += "No Emby User ID in Config File."

if config.has_option('emby', 'dirfrom'):
	embydirf = config.get('emby', 'dirfrom')
else:
	embydirf = ""

if config.has_option('emby', 'dirto'):
	embydirt = config.get('emby', 'dirto')
else:
	embydirt = ""

if len(embyapi) == 0 or len(embykey) == 0 or len(embyusr) == 0:
	print(embyerror)
	sys.exit()

if embyapi[-1] != "/":
	embyapi += "/"

url = embyapi + "Users/%s/Items?IncludeItemTypes=Movie&Recursive=true&StartIndex=0&format=json&fields=RemoteTrailers,Path" % (embyusr)

headers = {
        'content-type': 'application/json',
        'Authorization': 'MediaBrowser',
        'UserId': embyusr,
        'Client': 'EmbyTrailers',
        'Device': 'Python Script',
        'DeviceId': 'xxx',
        'Version': '1.0.0.0',
        'X-MediaBrowser-Token': embykey,
}

emby = requests.get(url, headers = headers)
films = json.loads(emby.content)

if not 'Name' in films['Items'][0]:
	print
	print("No Films Found")
else:
	for film in films['Items']:
		if film['LocalTrailerCount'] == 0:
			h += 1
			fulpath = film['Path'].encode("utf-8")
			
			if embydirf != "" and embydirt != "": 
				fulpath = fulpath.replace(embydirf, embydirt, 1)
				fulpath = os.path.normpath(fulpath.replace('\\', os.sep))
			
			basefil = os.path.basename(fulpath)
			basedir = os.path.dirname(fulpath)
			
			trail = os.path.splitext(basefil)[0]
			if trail[-3:-1] == "CD":
				trail = trail[:-4]
			
			if basedir[-1] != os.sep:
				basedir += os.sep
			
			if len(glob.glob(r"%s%s%s" % (basedir, trail, "-trailer*"))) == 0:
				download = True
				i += 1
				for trailer in film['RemoteTrailers']:
					if download:
						try:
							yt = YouTube(trailer['Url'].encode("utf-8"))
							yt.set_filename(trail + "-trailer")
							video = yt.filter('mp4')[-1]
							video.download(basedir)
						except Exception, e:
							print(trail + " - " + trailer['Url'].encode("utf-8") + " - " + str(e))
						if len(glob.glob(r"%s%s%s" % (basedir, trail, "-trailer*"))) > 0:
							download = False
							j += 1 
				if download:
					k += 1

	print("Emby thinks %s Films are Missing Trailers" % (h))
	print("%s Films Actually Missing Trailers" % (i))
	print("Downloaded %s Trailers" % (j))
	print("Unable to Download %s Trailers" % (k))

It reads a trailer.ini file:

[emby]
api = https://localhost/emby/
key = 00000000000000000000000000000000
usr = 11111111111111111111111111111111 
dirfrom = \\Server
dirto = /mnt

which contains the Emby URL, Emby Username, Emby Api Key and two keys to convert the file paths (Emby reports the file location as \\Server\Movie\Movie.avi, but when I want to run the script on the local machine I want to look at /mnt/Movie/Movie.avi)

 

Two assumptions the script makes is that the trailers are stored as <Movie Name>-trailer.mp4 and that files that have been split over multiple discs will be <Movie Name> CD1.avi, <Movie Name> CD2.avi etc

 

I've run the script locally on my FreeBSD server and remotely from a Windows Machine (In this instance I removed the dirfrom and dirto values) and seems to work on both.

 

Thought this might be useful to others or for someone wanting to work with Emby in Python.

Edited by llygoden
  • Like 1
Link to comment
Share on other sites

llygoden

I also wrote a script to list the films that still don't have trailers after the above script has been run. It uses the same config file for it's information.

import sys
import requests
import urllib
import json
import ConfigParser
import glob
import os

config = ConfigParser.RawConfigParser()
config.read('trailers.ini')

embyapi = ""
embykey = ""
embyusr = ""

if config.has_option('emby', 'api'):
	embyapi = config.get('emby', 'api')
else:
	embyerror = "No Emby URL in Config File.\n"

if config.has_option('emby', 'key'):
	embykey = config.get('emby', 'key')
else:
	embyerror += "No Emby API Key in Config File.\n"

if config.has_option('emby', 'usr'):
	embyusr = config.get('emby', 'usr')
else:
	embyerror += "No Emby User ID in Config File."

if config.has_option('emby', 'dirfrom'):
	embydirf = config.get('emby', 'dirfrom')
else:
	embydirf = ""

if config.has_option('emby', 'dirto'):
	embydirt = config.get('emby', 'dirto')
else:
	embydirt = ""

if len(embyapi) == 0 or len(embykey) == 0 or len(embyusr) == 0:
	print(embyerror)
	sys.exit()

if embyapi[-1] != "/":
	embyapi += "/"
	
url = embyapi + "Users/%s/Items?IncludeItemTypes=Movie&Recursive=true&StartIndex=0&format=json&fields=RemoteTrailers,Path" % (embyusr)

headers = {
        'content-type': 'application/json',
        'Authorization': 'MediaBrowser',
        'UserId': embyusr,
        'Client': 'EmbyTrailers',
        'Device': 'Python Script',
        'DeviceId': 'xxx',
        'Version': '1.0.0.0',
        'X-MediaBrowser-Token': embykey,
}

emby = requests.get(url, headers = headers)
films = json.loads(emby.content)

if not 'Name' in films['Items'][0]:
	print
	print("No Films Found")
else:
	i = 0
	for film in films['Items']:
		if film['LocalTrailerCount'] == 0:
			fulpath = film['Path'].encode("utf-8")
			
			if embydirf != "" and embydirt != "": 
				fulpath = fulpath.replace(embydirf, embydirt, 1)
				fulpath = os.path.normpath(fulpath.replace('\\', os.sep))
			
			basefil = os.path.basename(fulpath)
			basedir = os.path.dirname(fulpath)
			
			trail = os.path.splitext(basefil)[0]
			if trail[-3:-1] == "CD":
				trail = trail[:-4]
			
			if basedir[-1] != os.sep:
				basedir += os.sep
			
			if len(glob.glob(r"%s%s%s" % (basedir, trail, "-trailer*"))) == 0:
				i += 1
				print('{:>5}'.format(str(i) + '. ') + trail)
  • Like 1
Link to comment
Share on other sites

  • 4 weeks later...
zigzagtshirt

Excited to try this out.  Looks promising.

 

I am wondering though, if you are pulling trailers off YouTube (presumably the same ones Emby streams for you anyway), what's the rationale for saving them locally and using storage space?  Or, as you mentioned, you just wanted to learn some Python and Emby API usage?

 

Can't wait to try it out later :)

Edited by zigzagtshirt
Link to comment
Share on other sites

Having some issues getting these scripts to run - what version of Python are you using?

 

I'm getting strange JSON cannot be decoded messages, so I'm not sure where to start troubleshooting.

Link to comment
Share on other sites

  • 2 months later...
llygoden

Sorry for the long overdue response

 

@@zigzagtshirt

You're correct in that I wanted to learn a bit of Python, but also I have clients with devices that can't play the streamed YouTube trailers as they don't have native apps and so my understanding is that the only way for them to see trailers is with locally held files. 

 

@@JasonG

I'm using Python 2.7, if you post your error messages I'll attempt to see if I can help.

Link to comment
Share on other sites

  • 3 weeks later...
JasonG

Sorry for the long overdue response

 

@@zigzagtshirt

You're correct in that I wanted to learn a bit of Python, but also I have clients with devices that can't play the streamed YouTube trailers as they don't have native apps and so my understanding is that the only way for them to see trailers is with locally held files. 

 

@@JasonG

I'm using Python 2.7, if you post your error messages I'll attempt to see if I can help.

 

@llygoden

 

I'm receiving the following:


Traceback (most recent call last):
  File "trailercheck.py", line 58, in <module>
    films = json.loads(emby.content)
  File "C:\Python27\lib\json\__init__.py", line 339, in loads
    return _default_decoder.decode(s)
  File "C:\Python27\lib\json\decoder.py", line 364, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "C:\Python27\lib\json\decoder.py", line 382, in raw_decode
    raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded
Edited by JasonG
Link to comment
Share on other sites

  • 1 month later...
JasonG

 

@llygoden

 

I'm receiving the following:


Traceback (most recent call last):
  File "trailercheck.py", line 58, in <module>
    films = json.loads(emby.content)
  File "C:\Python27\lib\json\__init__.py", line 339, in loads
    return _default_decoder.decode(s)
  File "C:\Python27\lib\json\decoder.py", line 364, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "C:\Python27\lib\json\decoder.py", line 382, in raw_decode
    raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded

 

After troubleshooting, I was able to fix the issue. The URL line, for whatever reason, did not like the variable, so hardcoding my user's GUID worked.

Link to comment
Share on other sites

  • 6 months later...

Hello @@JasonG

 

Thanks for taking time to make this script!

 

I tried to use it on my nas Synology with python 2.7 installed but all downloads failed.


Here is a sample of the errors I got : 


Memento (2000) Bluray-1080p - https://www.youtube.com/watch?v=FzFQ768J7Bs - 'YouTube' object has no attribute 'set_filename'
Passengers (2016) Bluray-1080p - https://www.youtube.com/watch?v=LafZkUT-zkc - 'YouTube' object has no attribute 'set_filename'


Does the plugin still work? Maybe Youtube has changed something? 

Edited by kesm
  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...