migrated radarr

This commit is contained in:
Nicholas St. Germain 2018-12-01 20:33:33 -06:00
parent d1079ab949
commit bf1db64b82
8 changed files with 371 additions and 365 deletions

2
.gitignore vendored
View file

@ -9,7 +9,5 @@ __pycache__
GeoLite2-City.mmdb
GeoLite2-City.tar.gz
data/varken.ini
data/GeoLite2-City.mmdb
data/GeoLite2-City.tar.gz
.idea/
Legacy/configuration.py

View file

@ -1,171 +0,0 @@
# Do not edit this script. Edit configuration.py
import sys
import requests
from datetime import datetime, timezone
from influxdb import InfluxDBClient
import argparse
from argparse import RawTextHelpFormatter
from Legacy import configuration
def now_iso():
now_iso = datetime.now(timezone.utc).astimezone().isoformat()
return now_iso
def influx_sender(influx_payload):
influx = InfluxDBClient(configuration.influxdb_url, configuration.influxdb_port, configuration.influxdb_username,
configuration.influxdb_password, configuration.radarr_influxdb_db_name)
influx.write_points(influx_payload)
def get_missing_movies():
# Set the time here so we have one timestamp to work with
now = now_iso()
missing = []
influx_payload = []
for radarr_url, radarr_api_key, server_id in configuration.radarr_server_list:
headers = {'X-Api-Key': radarr_api_key}
get_movies = requests.get('{}/api/movie'.format(radarr_url), headers=headers).json()
movies = {d['tmdbId']: d for d in get_movies}
for movie in movies.keys():
if not movies[movie]['downloaded']:
movie_name = ('{} ({})'.format(movies[movie]['title'], movies[movie]['year']))
missing.append((movie_name, movies[movie]['tmdbId']))
for movie, id in missing:
influx_payload.append(
{
"measurement": "Radarr",
"tags": {
"type": "Missing",
"tmdbId": id,
"server": server_id
},
"time": now,
"fields": {
"name": movie
}
}
)
# Empty missing or else things get foo bared
missing = []
return influx_payload
def get_missing_avl():
# Set the time here so we have one timestamp to work with
now = now_iso()
missing = []
influx_payload = []
for radarr_url, radarr_api_key, server_id in configuration.radarr_server_list:
headers = {'X-Api-Key': radarr_api_key}
get_movies = requests.get('{}/api/movie'.format(radarr_url), headers=headers).json()
movies = {d['tmdbId']: d for d in get_movies}
for movie in movies.keys():
if not movies[movie]['downloaded']:
if movies[movie]['isAvailable'] is True:
movie_name = ('{} ({})'.format(movies[movie]['title'], movies[movie]['year']))
missing.append((movie_name, movies[movie]['tmdbId']))
for movie, id in missing:
influx_payload.append(
{
"measurement": "Radarr",
"tags": {
"type": "Missing_Available",
"tmdbId": id,
"server": server_id
},
"time": now,
"fields": {
"name": movie,
}
}
)
# Empty missing or else things get foo bared
missing = []
return influx_payload
def get_queue_movies():
# Set the time here so we have one timestamp to work with
now = now_iso()
influx_payload = []
queue = []
for radarr_url, radarr_api_key, server_id in configuration.radarr_server_list:
headers = {'X-Api-Key': radarr_api_key}
get_movies = requests.get('{}/api/queue'.format(radarr_url), headers=headers).json()
queue_movies = {d['id']: d for d in get_movies}
for movie in queue_movies.keys():
name = '{} ({})'.format(queue_movies[movie]['movie']['title'], queue_movies[movie]['movie']['year'])
quality = (queue_movies[movie]['quality']['quality']['name'])
protocol = (queue_movies[movie]['protocol'].upper())
if protocol == 'USENET':
protocol_id = 1
else:
protocol_id = 0
queue.append((name, queue_movies[movie]['id']))
for movie, id in queue:
influx_payload.append(
{
"measurement": "Radarr",
"tags": {
"type": "Queue",
"tmdbId": id,
"server": server_id
},
"time": now,
"fields": {
"name": movie,
"quality": quality,
"protocol": protocol,
"protocol_id": protocol_id
}
}
)
# Empty queue or else things get foo bared
queue = []
return influx_payload
if __name__ == "__main__":
parser = argparse.ArgumentParser(prog='Radarr stats operations',
description='Script to aid in data gathering from Radarr', formatter_class=RawTextHelpFormatter)
parser.add_argument("--missing", action='store_true',
help='Get missing movies')
parser.add_argument("--missing_avl", action='store_true',
help='Get missing yet available movies')
parser.add_argument("--queue", action='store_true',
help='Get movies in queue')
opts = parser.parse_args()
if opts.missing:
influx_sender(get_missing_movies())
elif opts.missing_avl:
influx_sender(get_missing_avl())
elif opts.queue:
influx_sender(get_queue_movies())
elif len(sys.argv) == 1:
parser.print_help(sys.stderr)
sys.exit(1)

View file

@ -25,7 +25,48 @@ class TVShow(NamedTuple):
id: int = None
class Movie(NamedTuple):
title: str = None
alternativeTitles: list = None
secondaryYearSourceId: int = None
sortTitle: str = None
sizeOnDisk: int = None
status: str = None
overview: str = None
inCinemas: str = None
images: list = None
downloaded: bool = None
year: int = None
secondaryYear: str = None
hasFile: bool = None
youTubeTrailerId: str = None
studio: str = None
path: str = None
profileId: int = None
pathState: str = None
monitored: bool = None
minimumAvailability: str = None
isAvailable: bool = None
folderName: str = None
runtime: int = None
lastInfoSync: str = None
cleanTitle: str = None
imdbId: str = None
tmdbId: int = None
titleSlug: str = None
genres: list = None
tags: list = None
added: str = None
ratings: dict = None
movieFile: dict = None
qualityProfileId: int = None
physicalRelease: str = None
physicalReleaseNote: str = None
website: str = None
id: int = None
class Queue(NamedTuple):
movie: dict = None
series: dict = None
episode: dict = None
quality: dict = None
@ -54,6 +95,15 @@ class SonarrServer(NamedTuple):
queue: bool = False
queue_run_seconds: int = 1
class RadarrServer(NamedTuple):
id: int = None
url: str = None
api_key: str = None
verify_ssl: bool = False
queue: bool = False
queue_run_seconds: int = 1
get_missing: bool = False
get_missing_run_seconds: int = 30
class Server(NamedTuple):
id: int = None
@ -82,193 +132,195 @@ class InfluxServer(NamedTuple):
class TautulliStream(NamedTuple):
rating: str
transcode_width: str
labels: list
stream_bitrate: str
bandwidth: str
optimized_version: int
video_language: str
parent_rating_key: str
rating_key: str
platform_version: str
transcode_hw_decoding: int
thumb: str
title: str
video_codec_level: str
tagline: str
last_viewed_at: str
audio_sample_rate: str
user_rating: str
platform: str
collections: list
location: str
transcode_container: str
audio_channel_layout: str
local: str
stream_subtitle_format: str
stream_video_ref_frames: str
transcode_hw_encode_title: str
stream_container_decision: str
audience_rating: str
full_title: str
ip_address: str
subtitles: int
stream_subtitle_language: str
channel_stream: int
video_bitrate: str
is_allow_sync: int
stream_video_bitrate: str
summary: str
stream_audio_decision: str
aspect_ratio: str
audio_bitrate_mode: str
transcode_hw_decode_title: str
stream_audio_channel_layout: str
deleted_user: int
library_name: str
art: str
stream_video_resolution: str
video_profile: str
sort_title: str
stream_video_codec_level: str
stream_video_height: str
year: str
stream_duration: str
stream_audio_channels: str
video_language_code: str
transcode_key: str
transcode_throttled: int
container: str
stream_audio_bitrate: str
user: str
selected: int
product_version: str
subtitle_location: str
transcode_hw_requested: int
video_height: str
state: str
is_restricted: int
email: str
stream_container: str
transcode_speed: str
video_bit_depth: str
stream_audio_sample_rate: str
grandparent_title: str
studio: str
transcode_decision: str
video_width: str
bitrate: str
machine_id: str
originally_available_at: str
video_frame_rate: str
synced_version_profile: str
friendly_name: str
audio_profile: str
optimized_version_title: str
platform_name: str
stream_video_language: str
keep_history: int
stream_audio_codec: str
stream_video_codec: str
grandparent_thumb: str
synced_version: int
transcode_hw_decode: str
user_thumb: str
stream_video_width: str
height: str
stream_subtitle_decision: str
audio_codec: str
parent_title: str
guid: str
audio_language_code: str
transcode_video_codec: str
transcode_audio_codec: str
stream_video_decision: str
user_id: int
transcode_height: str
transcode_hw_full_pipeline: int
throttled: str
quality_profile: str
width: str
live: int
stream_subtitle_forced: int
media_type: str
video_resolution: str
stream_subtitle_location: str
do_notify: int
video_ref_frames: str
stream_subtitle_language_code: str
audio_channels: str
stream_audio_language_code: str
optimized_version_profile: str
relay: int
duration: str
rating_image: str
is_home_user: int
is_admin: int
ip_address_public: str
allow_guest: int
transcode_audio_channels: str
stream_audio_channel_layout_: str
media_index: str
stream_video_framerate: str
transcode_hw_encode: str
grandparent_rating_key: str
original_title: str
added_at: str
banner: str
bif_thumb: str
parent_media_index: str
live_uuid: str
audio_language: str
stream_audio_bitrate_mode: str
username: str
subtitle_decision: str
children_count: str
updated_at: str
player: str
subtitle_format: str
file: str
file_size: str
session_key: str
id: str
subtitle_container: str
genres: list
stream_video_language_code: str
indexes: int
video_decision: str
stream_audio_language: str
writers: list
actors: list
progress_percent: str
audio_decision: str
subtitle_forced: int
profile: str
product: str
view_offset: str
type: str
audience_rating_image: str
audio_bitrate: str
section_id: str
stream_subtitle_codec: str
subtitle_codec: str
video_codec: str
device: str
stream_video_bit_depth: str
video_framerate: str
transcode_hw_encoding: int
transcode_protocol: str
shared_libraries: list
stream_aspect_ratio: str
content_rating: str
session_id: str
directors: list
parent_thumb: str
subtitle_language_code: str
transcode_progress: int
subtitle_language: str
stream_subtitle_container: str
rating: str = None
transcode_width: str = None
labels: list = None
stream_bitrate: str = None
bandwidth: str = None
optimized_version: int = None
video_language: str = None
parent_rating_key: str = None
rating_key: str = None
platform_version: str = None
transcode_hw_decoding: int = None
thumb: str = None
title: str = None
video_codec_level: str = None
tagline: str = None
last_viewed_at: str = None
audio_sample_rate: str = None
user_rating: str = None
platform: str = None
collections: list = None
location: str = None
transcode_container: str = None
audio_channel_layout: str = None
local: str = None
stream_subtitle_format: str = None
stream_video_ref_frames: str = None
transcode_hw_encode_title: str = None
stream_container_decision: str = None
audience_rating: str = None
full_title: str = None
ip_address: str = None
subtitles: int = None
stream_subtitle_language: str = None
channel_stream: int = None
video_bitrate: str = None
is_allow_sync: int = None
stream_video_bitrate: str = None
summary: str = None
stream_audio_decision: str = None
aspect_ratio: str = None
audio_bitrate_mode: str = None
transcode_hw_decode_title: str = None
stream_audio_channel_layout: str = None
deleted_user: int = None
library_name: str = None
art: str = None
stream_video_resolution: str = None
video_profile: str = None
sort_title: str = None
stream_video_codec_level: str = None
stream_video_height: str = None
year: str = None
stream_duration: str = None
stream_audio_channels: str = None
video_language_code: str = None
transcode_key: str = None
transcode_throttled: int = None
container: str = None
stream_audio_bitrate: str = None
user: str = None
selected: int = None
product_version: str = None
subtitle_location: str = None
transcode_hw_requested: int = None
video_height: str = None
state: str = None
is_restricted: int = None
email: str = None
stream_container: str = None
transcode_speed: str = None
video_bit_depth: str = None
stream_audio_sample_rate: str = None
grandparent_title: str = None
studio: str = None
transcode_decision: str = None
video_width: str = None
bitrate: str = None
machine_id: str = None
originally_available_at: str = None
video_frame_rate: str = None
synced_version_profile: str = None
friendly_name: str = None
audio_profile: str = None
optimized_version_title: str = None
platform_name: str = None
stream_video_language: str = None
keep_history: int = None
stream_audio_codec: str = None
stream_video_codec: str = None
grandparent_thumb: str = None
synced_version: int = None
transcode_hw_decode: str = None
user_thumb: str = None
stream_video_width: str = None
height: str = None
stream_subtitle_decision: str = None
audio_codec: str = None
parent_title: str = None
guid: str = None
audio_language_code: str = None
transcode_video_codec: str = None
transcode_audio_codec: str = None
stream_video_decision: str = None
user_id: int = None
transcode_height: str = None
transcode_hw_full_pipeline: int = None
throttled: str = None
quality_profile: str = None
width: str = None
live: int = None
stream_subtitle_forced: int = None
media_type: str = None
video_resolution: str = None
stream_subtitle_location: str = None
do_notify: int = None
video_ref_frames: str = None
stream_subtitle_language_code: str = None
audio_channels: str = None
stream_audio_language_code: str = None
optimized_version_profile: str = None
relay: int = None
duration: str = None
rating_image: str = None
is_home_user: int = None
is_admin: int = None
ip_address_public: str = None
allow_guest: int = None
transcode_audio_channels: str = None
stream_audio_channel_layout_: str = None
media_index: str = None
stream_video_framerate: str = None
transcode_hw_encode: str = None
grandparent_rating_key: str = None
original_title: str = None
added_at: str = None
banner: str = None
bif_thumb: str = None
parent_media_index: str = None
live_uuid: str = None
audio_language: str = None
stream_audio_bitrate_mode: str = None
username: str = None
subtitle_decision: str = None
children_count: str = None
updated_at: str = None
player: str = None
subtitle_format: str = None
file: str = None
file_size: str = None
session_key: str = None
id: str = None
subtitle_container: str = None
genres: list = None
stream_video_language_code: str = None
indexes: int = None
video_decision: str = None
stream_audio_language: str = None
writers: list = None
actors: list = None
progress_percent: str = None
audio_decision: str = None
subtitle_forced: int = None
profile: str = None
product: str = None
view_offset: str = None
type: str = None
audience_rating_image: str = None
audio_bitrate: str = None
section_id: str = None
stream_subtitle_codec: str = None
subtitle_codec: str = None
video_codec: str = None
device: str = None
stream_video_bit_depth: str = None
video_framerate: str = None
transcode_hw_encoding: int = None
transcode_protocol: str = None
shared_libraries: list = None
stream_aspect_ratio: str = None
content_rating: str = None
session_id: str = None
directors: list = None
parent_thumb: str = None
subtitle_language_code: str = None
transcode_progress: int = None
subtitle_language: str = None
stream_subtitle_container: str = None
_cache_time: int = None
def geoip_download():
tar_dbfile = abspath(join('.', 'data', 'GeoLite2-City.tar.gz'))

View file

@ -1,7 +1,7 @@
import sys
import configparser
from os.path import abspath, join
from Varken.helpers import Server, TautulliServer, SonarrServer, InfluxServer
from Varken.helpers import Server, TautulliServer, SonarrServer, InfluxServer, RadarrServer
class INIParser(object):
@ -81,7 +81,7 @@ class INIParser(object):
except ValueError:
self.radarr_enabled = True
if self.sonarr_enabled:
if self.radarr_enabled:
sids = self.config.get('global', 'radarr_server_ids').strip(' ').split(',')
for server_id in sids:
@ -90,8 +90,14 @@ class INIParser(object):
apikey = self.config.get(radarr_section, 'apikey')
scheme = 'https://' if self.config.getboolean(radarr_section, 'ssl') else 'http://'
verify_ssl = self.config.getboolean(radarr_section, 'verify_ssl')
queue = self.config.getboolean(radarr_section, 'queue')
queue_run_seconds = self.config.getint(radarr_section, 'queue_run_seconds')
get_missing = self.config.getboolean(radarr_section, 'get_missing')
get_missing_run_seconds = self.config.getint(radarr_section, 'get_missing_run_seconds')
self.radarr_servers.append(Server(server_id, scheme + url, apikey, verify_ssl))
server = RadarrServer(server_id, scheme + url, apikey, verify_ssl, queue, queue_run_seconds,
get_missing, get_missing_run_seconds)
self.radarr_servers.append(server)
# Parse Tautulli options
try:

106
Varken/radarr.py Normal file
View file

@ -0,0 +1,106 @@
import requests
from datetime import datetime, timezone
from influxdb import InfluxDBClient
from Varken.logger import logging
from Varken.helpers import Movie, Queue
class RadarrAPI(object):
def __init__(self, servers, influx_server):
self.now = datetime.now(timezone.utc).astimezone().isoformat()
self.influx = InfluxDBClient(influx_server.url, influx_server.port, influx_server.username,
influx_server.password, 'plex2')
self.servers = servers
# Create session to reduce server web thread load, and globally define pageSize for all requests
self.session = requests.Session()
def influx_push(self, payload):
# TODO: error handling for failed connection
self.influx.write_points(payload)
@logging
def get_missing(self, notimplemented):
endpoint = '/api/movie'
self.now = datetime.now(timezone.utc).astimezone().isoformat()
influx_payload = []
for server in self.servers:
missing = []
headers = {'X-Api-Key': server.api_key}
get = self.session.get(server.url + endpoint, headers=headers, verify=server.verify_ssl).json()
movies = [Movie(**movie) for movie in get]
for movie in movies:
if server.get_missing:
if not movie.downloaded and movie.isAvailable:
ma = True
else:
ma = False
movie_name = '{} ({})'.format(movie.title, movie.year)
missing.append((movie_name, ma, movie.tmdbId))
for title, ma, mid in missing:
influx_payload.append(
{
"measurement": "Radarr",
"tags": {
"Missing": True,
"Missing_Available": ma,
"tmdbId": mid,
"server": server.id
},
"time": self.now,
"fields": {
"name": title
}
}
)
self.influx_push(influx_payload)
@logging
def get_queue(self, notimplemented):
endpoint = '/api/queue'
self.now = datetime.now(timezone.utc).astimezone().isoformat()
influx_payload = []
for server in self.servers:
queue = []
headers = {'X-Api-Key': server.api_key}
get = self.session.get(server.url + endpoint, headers=headers, verify=server.verify_ssl).json()
for movie in get:
movie['movie'] = Movie(**movie['movie'])
download_queue = [Queue(**movie) for movie in get]
for queue_item in download_queue:
name = '{} ({})'.format(queue_item.movie.title, queue_item.movie.year)
if queue_item.protocol.upper() == 'USENET':
protocol_id = 1
else:
protocol_id = 0
queue.append((name, queue_item.quality['quality']['name'], queue_item.protocol.upper(),
protocol_id, queue_item.id))
for movie, quality, protocol, protocol_id, qid in queue:
influx_payload.append(
{
"measurement": "Radarr",
"tags": {
"type": "Queue",
"tmdbId": qid,
"server": server.id
},
"time": self.now,
"fields": {
"name": movie,
"quality": quality,
"protocol": protocol,
"protocol_id": protocol_id
}
}
)
self.influx_push(influx_payload)

View file

@ -1,5 +1,3 @@
#!/usr/bin/env python3
# Do not edit this script. Edit configuration.py
import requests
from influxdb import InfluxDBClient
from datetime import datetime, timezone, date, timedelta

View file

@ -58,6 +58,11 @@ url = radarr1.domain.tld
apikey = xxxxxxxxxxxxxxxx
ssl = false
verify_ssl = true
queue = true
queue_run_seconds = 300
get_missing = true
get_missing_available = true
get_missing_run_seconds = 300
[radarr-2]
url = radarr2.domain.tld

View file

@ -5,6 +5,7 @@ from time import sleep
from Varken.iniparser import INIParser
from Varken.sonarr import SonarrAPI
from Varken.tautulli import TautulliAPI
from Varken.radarr import RadarrAPI
def threaded(job, days=None):
@ -37,6 +38,17 @@ if __name__ == "__main__":
if server.get_sessions:
schedule.every(server.get_sessions_run_seconds).seconds.do(threaded, TAUTULLI.get_sessions)
if CONFIG.radarr_enabled:
RADARR = RadarrAPI(CONFIG.radarr_servers, CONFIG.influx_server)
for server in CONFIG.radarr_servers:
if any([server.get_missing, server.get_missing_available]):
schedule.every(server.get_missing_run_seconds).seconds.do(threaded, RADARR.get_missing)
if server.queue:
schedule.every(server.queue_run_seconds).seconds.do(threaded, RADARR.get_queue)
while True:
schedule.run_pending()
sleep(1)