Compare commits
3 Commits
development
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f7c88e96e | |||
| a80c692786 | |||
| 613a52d3da |
@@ -19,9 +19,5 @@ dbsync.log.*
|
|||||||
.idea
|
.idea
|
||||||
/log
|
/log
|
||||||
/venv
|
/venv
|
||||||
/temp/post_tvmaze_updates.json
|
|
||||||
/temp/post_tvmaze_updates.json.*
|
/temp/post_tvmaze_updates.json.*
|
||||||
/temp/tvmaze_updates.json
|
|
||||||
/temp.skip_ids.txt
|
|
||||||
/archived
|
/archived
|
||||||
settings/tvsync_settings.cfg
|
|
||||||
|
|||||||
@@ -1,270 +0,0 @@
|
|||||||
"""MongoDB query utilities for filtering documents by update timestamp."""
|
|
||||||
|
|
||||||
__author__ = 'Wendell Jones'
|
|
||||||
|
|
||||||
from typing import Any, Dict, List, Optional
|
|
||||||
from datetime import datetime, timedelta, timezone
|
|
||||||
import logging
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class MongoQueryUtils:
|
|
||||||
"""Utilities for querying MongoDB collections."""
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_recent_updates(
|
|
||||||
collection: Any,
|
|
||||||
minutes: int = 60,
|
|
||||||
updated_field: str = 'updated',
|
|
||||||
query_filter: Optional[Dict] = None,
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""
|
|
||||||
Find documents updated in the last N minutes.
|
|
||||||
|
|
||||||
Handles both epoch timestamps (int/float) and ISO datetime strings.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
collection: PyMongo collection object.
|
|
||||||
minutes: Number of minutes to look back (default: 60).
|
|
||||||
updated_field: Name of the updated timestamp field (default: 'updated').
|
|
||||||
query_filter: Additional MongoDB query filter to apply (optional).
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List of documents updated within the time window.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
from db.query_utils import MongoQueryUtils
|
|
||||||
|
|
||||||
recent = MongoQueryUtils.get_recent_updates(
|
|
||||||
collection=mongo_db['series'],
|
|
||||||
minutes=60,
|
|
||||||
updated_field='updated',
|
|
||||||
)
|
|
||||||
print(f"Found {len(recent)} recently updated documents")
|
|
||||||
"""
|
|
||||||
if query_filter is None:
|
|
||||||
query_filter = {}
|
|
||||||
|
|
||||||
# Calculate cutoff time (60 minutes ago)
|
|
||||||
now = datetime.now(timezone.utc)
|
|
||||||
cutoff_time = now - timedelta(minutes=minutes)
|
|
||||||
cutoff_epoch = int(cutoff_time.timestamp())
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
f"Searching for documents updated in last {minutes} minutes "
|
|
||||||
f"(since {cutoff_time.isoformat()})"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Build query for documents updated after cutoff
|
|
||||||
# Support both epoch timestamps and ISO datetime strings
|
|
||||||
query = {
|
|
||||||
**query_filter,
|
|
||||||
'$or': [
|
|
||||||
# Epoch timestamp (int/float)
|
|
||||||
{updated_field: {'$gte': cutoff_epoch}},
|
|
||||||
# ISO datetime string
|
|
||||||
{updated_field: {'$gte': cutoff_time.isoformat()}},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
documents = list(collection.find(query))
|
|
||||||
logger.info(f"Found {len(documents)} documents matching criteria")
|
|
||||||
|
|
||||||
return documents
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_recent_updates_by_id(
|
|
||||||
collection: Any,
|
|
||||||
minutes: int = 60,
|
|
||||||
updated_field: str = 'updated',
|
|
||||||
id_field: str = 'id',
|
|
||||||
query_filter: Optional[Dict] = None,
|
|
||||||
) -> List[int]:
|
|
||||||
"""
|
|
||||||
Get IDs of documents updated in the last N minutes.
|
|
||||||
|
|
||||||
Useful for batch processing or sync operations.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
collection: PyMongo collection object.
|
|
||||||
minutes: Number of minutes to look back (default: 60).
|
|
||||||
updated_field: Name of the updated timestamp field (default: 'updated').
|
|
||||||
id_field: Name of the ID field (default: 'id').
|
|
||||||
query_filter: Additional MongoDB query filter (optional).
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List of document IDs that were recently updated.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
ids = MongoQueryUtils.get_recent_updates_by_id(
|
|
||||||
collection=mongo_db['series'],
|
|
||||||
minutes=60,
|
|
||||||
)
|
|
||||||
print(f"Series IDs to update: {ids}")
|
|
||||||
"""
|
|
||||||
docs = MongoQueryUtils.get_recent_updates(
|
|
||||||
collection=collection,
|
|
||||||
minutes=minutes,
|
|
||||||
updated_field=updated_field,
|
|
||||||
query_filter=query_filter,
|
|
||||||
)
|
|
||||||
|
|
||||||
ids = [doc.get(id_field) for doc in docs if id_field in doc]
|
|
||||||
return ids
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_updates_since(
|
|
||||||
collection: Any,
|
|
||||||
since_timestamp: Optional[Any] = None,
|
|
||||||
minutes: int = 60,
|
|
||||||
updated_field: str = 'updated',
|
|
||||||
query_filter: Optional[Dict] = None,
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""
|
|
||||||
Find documents updated since a specific timestamp or N minutes ago.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
collection: PyMongo collection object.
|
|
||||||
since_timestamp: Epoch int, float, or ISO datetime string (optional).
|
|
||||||
If None, defaults to N minutes ago (see minutes parameter).
|
|
||||||
minutes: If since_timestamp is None, look back this many minutes (default: 60).
|
|
||||||
updated_field: Name of the updated timestamp field (default: 'updated').
|
|
||||||
query_filter: Additional MongoDB query filter (optional).
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List of documents updated since the timestamp.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
# Get updates since a specific timestamp
|
|
||||||
cutoff = 1704067200 # Epoch timestamp
|
|
||||||
docs = MongoQueryUtils.get_updates_since(
|
|
||||||
collection=mongo_db['series'],
|
|
||||||
since_timestamp=cutoff,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Or use default 60 minutes ago
|
|
||||||
docs = MongoQueryUtils.get_updates_since(
|
|
||||||
collection=mongo_db['series'],
|
|
||||||
)
|
|
||||||
|
|
||||||
# Or specify different time window
|
|
||||||
docs = MongoQueryUtils.get_updates_since(
|
|
||||||
collection=mongo_db['series'],
|
|
||||||
minutes=120, # Last 2 hours
|
|
||||||
)
|
|
||||||
"""
|
|
||||||
if query_filter is None:
|
|
||||||
query_filter = {}
|
|
||||||
|
|
||||||
# If no timestamp provided, use minutes parameter to calculate cutoff
|
|
||||||
if since_timestamp is None:
|
|
||||||
now = datetime.now(timezone.utc)
|
|
||||||
cutoff_time = now - timedelta(minutes=minutes)
|
|
||||||
since_timestamp = int(cutoff_time.timestamp())
|
|
||||||
logger.info(f"Using default {minutes} minutes ago: {cutoff_time.isoformat()}")
|
|
||||||
|
|
||||||
query = {
|
|
||||||
**query_filter,
|
|
||||||
'$or': [
|
|
||||||
{updated_field: {'$gte': since_timestamp}},
|
|
||||||
{updated_field: {'$gte': str(since_timestamp)}},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
documents = list(collection.find(query))
|
|
||||||
logger.info(f"Found {len(documents)} documents updated since {since_timestamp}")
|
|
||||||
|
|
||||||
return documents
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def count_recent_updates(
|
|
||||||
collection: Any,
|
|
||||||
minutes: int = 60,
|
|
||||||
updated_field: str = 'updated',
|
|
||||||
query_filter: Optional[Dict] = None,
|
|
||||||
) -> int:
|
|
||||||
"""
|
|
||||||
Count documents updated in the last N minutes.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
collection: PyMongo collection object.
|
|
||||||
minutes: Number of minutes to look back (default: 60).
|
|
||||||
updated_field: Name of the updated timestamp field (default: 'updated').
|
|
||||||
query_filter: Additional MongoDB query filter (optional).
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Count of recently updated documents.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
count = MongoQueryUtils.count_recent_updates(
|
|
||||||
collection=mongo_db['series'],
|
|
||||||
minutes=60,
|
|
||||||
)
|
|
||||||
print(f"{count} series were updated in the last hour")
|
|
||||||
"""
|
|
||||||
if query_filter is None:
|
|
||||||
query_filter = {}
|
|
||||||
|
|
||||||
now = datetime.now(timezone.utc)
|
|
||||||
cutoff_time = now - timedelta(minutes=minutes)
|
|
||||||
cutoff_epoch = int(cutoff_time.timestamp())
|
|
||||||
|
|
||||||
query = {
|
|
||||||
**query_filter,
|
|
||||||
'$or': [
|
|
||||||
{updated_field: {'$gte': cutoff_epoch}},
|
|
||||||
{updated_field: {'$gte': cutoff_time.isoformat()}},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
count = collection.count_documents(query)
|
|
||||||
return count
|
|
||||||
|
|
||||||
|
|
||||||
# Convenience functions
|
|
||||||
def get_recent_updates(
|
|
||||||
collection: Any,
|
|
||||||
minutes: int = 60,
|
|
||||||
updated_field: str = 'updated',
|
|
||||||
query_filter: Optional[Dict] = None,
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""Find documents updated in the last N minutes."""
|
|
||||||
return MongoQueryUtils.get_recent_updates(
|
|
||||||
collection=collection,
|
|
||||||
minutes=minutes,
|
|
||||||
updated_field=updated_field,
|
|
||||||
query_filter=query_filter,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_recent_ids(
|
|
||||||
collection: Any,
|
|
||||||
minutes: int = 60,
|
|
||||||
updated_field: str = 'updated',
|
|
||||||
id_field: str = 'id',
|
|
||||||
query_filter: Optional[Dict] = None,
|
|
||||||
) -> List[int]:
|
|
||||||
"""Get IDs of documents updated in the last N minutes."""
|
|
||||||
return MongoQueryUtils.get_recent_updates_by_id(
|
|
||||||
collection=collection,
|
|
||||||
minutes=minutes,
|
|
||||||
updated_field=updated_field,
|
|
||||||
id_field=id_field,
|
|
||||||
query_filter=query_filter,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def count_recent(
|
|
||||||
collection: Any,
|
|
||||||
minutes: int = 60,
|
|
||||||
updated_field: str = 'updated',
|
|
||||||
query_filter: Optional[Dict] = None,
|
|
||||||
) -> int:
|
|
||||||
"""Count documents updated in the last N minutes."""
|
|
||||||
return MongoQueryUtils.count_recent_updates(
|
|
||||||
collection=collection,
|
|
||||||
minutes=minutes,
|
|
||||||
updated_field=updated_field,
|
|
||||||
query_filter=query_filter,
|
|
||||||
)
|
|
||||||
+2
-91
@@ -335,7 +335,7 @@ def create_postgres_table_from_mongo(
|
|||||||
class MongoDocumentInserter:
|
class MongoDocumentInserter:
|
||||||
"""Insert MongoDB documents into PostgreSQL tables with type conversion."""
|
"""Insert MongoDB documents into PostgreSQL tables with type conversion."""
|
||||||
|
|
||||||
def __init__(self, batch_size: int = 10000):
|
def __init__(self, batch_size: int = 1000):
|
||||||
"""
|
"""
|
||||||
Initialize the document inserter.
|
Initialize the document inserter.
|
||||||
|
|
||||||
@@ -530,25 +530,8 @@ class MongoDocumentInserter:
|
|||||||
if on_conflict:
|
if on_conflict:
|
||||||
insert_sql += f" {on_conflict}"
|
insert_sql += f" {on_conflict}"
|
||||||
|
|
||||||
# Use different strategies depending on whether ON CONFLICT is needed.
|
|
||||||
# For ON CONFLICT updates, per-row execution is slow; use psycopg2's
|
|
||||||
# execute_values to perform fast multi-row INSERT ... VALUES (...) ON CONFLICT ...
|
|
||||||
if on_conflict:
|
|
||||||
try:
|
|
||||||
# Prepare ordered tuples for insertion
|
|
||||||
values_list = []
|
|
||||||
for doc in prepared:
|
|
||||||
values_list.append(tuple(doc.get(col) for col in column_names))
|
|
||||||
|
|
||||||
# Build base INSERT with placeholder for execute_values
|
|
||||||
insert_base = f"INSERT INTO {full_table_name} ({cols_str}) VALUES %s {on_conflict}"
|
|
||||||
|
|
||||||
# Use raw DB-API connection for execute_values
|
|
||||||
try:
|
|
||||||
import psycopg2
|
|
||||||
except Exception:
|
|
||||||
# Fall back to SQLAlchemy executemany if psycopg2 not available
|
|
||||||
with engine.begin() as conn:
|
with engine.begin() as conn:
|
||||||
|
if on_conflict:
|
||||||
for doc in prepared:
|
for doc in prepared:
|
||||||
try:
|
try:
|
||||||
conn.execute(text(insert_sql), [doc])
|
conn.execute(text(insert_sql), [doc])
|
||||||
@@ -558,78 +541,6 @@ class MongoDocumentInserter:
|
|||||||
raise
|
raise
|
||||||
logger.info(f"Inserted {total_inserted} documents into {full_table_name}")
|
logger.info(f"Inserted {total_inserted} documents into {full_table_name}")
|
||||||
else:
|
else:
|
||||||
# Use a single staging temporary table per insert_documents call.
|
|
||||||
# Stream each batch into the temp table via COPY, then run one
|
|
||||||
# INSERT ... SELECT ... ON CONFLICT ... to upsert all rows.
|
|
||||||
raw_conn = engine.raw_connection()
|
|
||||||
try:
|
|
||||||
cur = raw_conn.cursor()
|
|
||||||
import io
|
|
||||||
import csv
|
|
||||||
import time
|
|
||||||
import os
|
|
||||||
|
|
||||||
temp_name = f"tmp_{table_name}_{int(time.time())}_{os.getpid()}"
|
|
||||||
try:
|
|
||||||
# Create temporary table once
|
|
||||||
cur.execute(f"CREATE TEMP TABLE {temp_name} (LIKE {full_table_name} INCLUDING ALL);")
|
|
||||||
# Stream batches into temp table
|
|
||||||
for i in range(0, len(prepared), self.batch_size):
|
|
||||||
batch = prepared[i:i + self.batch_size]
|
|
||||||
try:
|
|
||||||
sio = io.StringIO()
|
|
||||||
writer = csv.writer(sio)
|
|
||||||
for doc in batch:
|
|
||||||
row = []
|
|
||||||
for col in column_names:
|
|
||||||
val = doc.get(col)
|
|
||||||
if val is None:
|
|
||||||
row.append('\\N')
|
|
||||||
else:
|
|
||||||
row.append(val)
|
|
||||||
writer.writerow(row)
|
|
||||||
sio.seek(0)
|
|
||||||
|
|
||||||
copy_sql = f"COPY {temp_name} ({cols_str}) FROM STDIN WITH CSV NULL '\\N'"
|
|
||||||
cur.copy_expert(copy_sql, sio)
|
|
||||||
raw_conn.commit()
|
|
||||||
batch_count = len(batch)
|
|
||||||
total_inserted += batch_count
|
|
||||||
logger.info(f"Copied {batch_count} rows into staging {temp_name} (total staged: {total_inserted})")
|
|
||||||
except Exception as e:
|
|
||||||
raw_conn.rollback()
|
|
||||||
logger.error(f"Error copying batch into {temp_name}: {e}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
# Perform a single upsert from the staging table
|
|
||||||
try:
|
|
||||||
insert_from_temp = f"INSERT INTO {full_table_name} ({cols_str}) SELECT {cols_str} FROM {temp_name} {on_conflict}"
|
|
||||||
cur.execute(insert_from_temp)
|
|
||||||
raw_conn.commit()
|
|
||||||
logger.info(f"Upserted staged rows from {temp_name} into {full_table_name}")
|
|
||||||
except Exception as e:
|
|
||||||
raw_conn.rollback()
|
|
||||||
logger.error(f"Error upserting from {temp_name} into {full_table_name}: {e}")
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
# Temp table will be dropped on commit/connection close, ensure commit
|
|
||||||
try:
|
|
||||||
raw_conn.commit()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
try:
|
|
||||||
cur.close()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
raw_conn.close()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
except Exception:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
with engine.begin() as conn:
|
|
||||||
for i in range(0, len(prepared), self.batch_size):
|
for i in range(0, len(prepared), self.batch_size):
|
||||||
batch = prepared[i:i + self.batch_size]
|
batch = prepared[i:i + self.batch_size]
|
||||||
try:
|
try:
|
||||||
|
|||||||
+1
-47
@@ -1,10 +1,9 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import requests
|
import requests
|
||||||
import time
|
import time
|
||||||
import logging
|
|
||||||
|
|
||||||
def download_json_to_file(api_url, outputdir, output_file, lprint):
|
def download_json_to_file(api_url, outputdir, output_file, lprint):
|
||||||
"""
|
"""
|
||||||
@@ -21,48 +20,3 @@ def download_json_to_file(api_url, outputdir, output_file, lprint):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
lprint.logprint("info", f"Downloadinng {outputdir}{output_file} for series ID: {output_file.replace('.json', '')} failed: {e}")
|
lprint.logprint("info", f"Downloadinng {outputdir}{output_file} for series ID: {output_file.replace('.json', '')} failed: {e}")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
# Download data from an API endpoint to a specified file in JSON format
|
|
||||||
api_url = 'https://sample-api.com/data'
|
|
||||||
outputdir = '/home/user/'
|
|
||||||
outputfile = 'data.json'
|
|
||||||
|
|
||||||
lprint = Logprint('output')
|
|
||||||
try:
|
|
||||||
download_json_to_file(api_url, outputdir, outputfile)
|
|
||||||
print("Download successful!")
|
|
||||||
except Exception as e:
|
|
||||||
# Handle any exceptions that occur during the download process and log them
|
|
||||||
lprint.logprint("info", f"Downloading {outputdir}{outputfile} failed with error:{e}")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__) # Use __name__ as the name for the logger
|
|
||||||
|
|
||||||
formatter = logging.Formatter('%(asctime)s:%(levelname)s:%(message)s')
|
|
||||||
|
|
||||||
fh = logging.FileHandler('logfilename.txt') # Create a file handler object
|
|
||||||
fh.setFormatter(formatter)
|
|
||||||
|
|
||||||
sh = logging.StreamHandler() # Create a stream handler object (for console output)
|
|
||||||
sh.setFormatter(formatter)
|
|
||||||
|
|
||||||
|
|
||||||
logger.setLevel(logging.INFO) # Set the level of messages to log. Possible values are DEBUG, INFO, WARNING, ERROR, and CRITICAL. Default is INFO
|
|
||||||
|
|
||||||
if not logger.hasHandlers():
|
|
||||||
logger.addHandler(fh) # Add file handler object to the logger object
|
|
||||||
if not logger.handlers:
|
|
||||||
logger.addHandler(sh) # Add stream handler object to the logger object
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
download_json_to_file() # Your function that you want to log the output of
|
|
||||||
except Exception as e: # Handle exceptions if any occur
|
|
||||||
logger.error(f"Error while running {download_json_to_file}: {e}")
|
|
||||||
else:
|
|
||||||
# If no exceptions are encountered, write a INFO message to the log file
|
|
||||||
logger.info(f"{download_json_to_file} completed successfully.")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|||||||
+2
-2
@@ -44,12 +44,12 @@ class LPrint:
|
|||||||
"""Check the log file size and rotate if necessary."""
|
"""Check the log file size and rotate if necessary."""
|
||||||
log_size = os.path.getsize(self.logfile)
|
log_size = os.path.getsize(self.logfile)
|
||||||
print(f'Logfile Size: {log_size} ({self.humanbytes(log_size)})')
|
print(f'Logfile Size: {log_size} ({self.humanbytes(log_size)})')
|
||||||
if force_rotate or log_size > 50_000_000: # 10 MB
|
if force_rotate or log_size > 10_000_000: # 10 MB
|
||||||
self.rotate_logs()
|
self.rotate_logs()
|
||||||
|
|
||||||
def rotate_logs(self):
|
def rotate_logs(self):
|
||||||
"""Rotate the log file."""
|
"""Rotate the log file."""
|
||||||
from logs.logrotater import LogRotate # Assuming logrotater is a custom module
|
from logrotater import LogRotate # Assuming logrotater is a custom module
|
||||||
rotater = LogRotate(prefix=self.logfile, verbose=True)
|
rotater = LogRotate(prefix=self.logfile, verbose=True)
|
||||||
try:
|
try:
|
||||||
rotater.rotate()
|
rotater.rotate()
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
''' A python logrotate library'''
|
|
||||||
|
|
||||||
import os
|
|
||||||
import glob
|
|
||||||
|
|
||||||
__author__ = 'Kyle Laplante'
|
|
||||||
__copyright__ = 'Copyright 2013, Kyle Laplante'
|
|
||||||
__date__ = '07-07-2013'
|
|
||||||
|
|
||||||
__license__ = 'GPL'
|
|
||||||
__version__ = '1.3'
|
|
||||||
__email__ = 'kyle.laplante@gmail.com'
|
|
||||||
|
|
||||||
|
|
||||||
class LogRotateException(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class LogRotate:
|
|
||||||
|
|
||||||
"""This library is a simple logrotater (or file rotater).
|
|
||||||
Simply pass in the path to the main logfile and this
|
|
||||||
library will rotate all the logs by an increment of 1.
|
|
||||||
Example:
|
|
||||||
|
|
||||||
import logrotater
|
|
||||||
|
|
||||||
rotater = logrotater.LogRotate(prefix='/home/kyle/p4.log', verbose=True)
|
|
||||||
|
|
||||||
rotater.rotate()
|
|
||||||
|
|
||||||
The prefix path should be the path of the main logfile without the .N extension.
|
|
||||||
The previous example would rotate /home/kyle/p4.log.N to /home/kyle/p4.log.N+1,
|
|
||||||
move /home/kyle/p4.log to /home/kyle/p4.log.1 and create a new empty /home/kyle/p4.log."""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
|
|
||||||
'''Required kwargs: prefix="some path"
|
|
||||||
Optional kwargs: verbose=True
|
|
||||||
If verbose is True then LogRotate will print what it is doing with each file.'''
|
|
||||||
|
|
||||||
self.verbose = kwargs.get('verbose', False)
|
|
||||||
for k, v in kwargs.items():
|
|
||||||
setattr(self, k, v)
|
|
||||||
if not os.path.isfile(self.prefix):
|
|
||||||
raise LogRotateException("Must use a valid prefix filename")
|
|
||||||
|
|
||||||
def rotate(self, *args, **kwargs):
|
|
||||||
self.files = glob.glob(self.prefix + "*")
|
|
||||||
if not len(self.files) > 0:
|
|
||||||
raise LogRotateException(
|
|
||||||
"No files with the prefix %s found" % self.prefix)
|
|
||||||
self.nums = []
|
|
||||||
for file in self.files:
|
|
||||||
try:
|
|
||||||
self.nums.append(int(file.split(self.prefix + '.')[-1]))
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
self.nums.sort()
|
|
||||||
self.nums.reverse()
|
|
||||||
for num in self.nums:
|
|
||||||
new_num = num + 1
|
|
||||||
if self.verbose:
|
|
||||||
print("moving %s.%d to %s.%d" % (self.prefix, num, self.prefix, new_num))
|
|
||||||
os.rename("%s.%d" % (self.prefix, num), "%s.%d" %
|
|
||||||
(self.prefix, new_num))
|
|
||||||
if self.verbose:
|
|
||||||
print("moving %s to %s.1" % (self.prefix, self.prefix))
|
|
||||||
os.rename(self.prefix, self.prefix + '.1')
|
|
||||||
self.touch(self.prefix)
|
|
||||||
|
|
||||||
def touch(self, path):
|
|
||||||
|
|
||||||
'''a simple method to touch a file'''
|
|
||||||
|
|
||||||
if self.verbose:
|
|
||||||
print("Creating file: %s" % path)
|
|
||||||
f = open(path, 'a')
|
|
||||||
os.utime(path, None)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
|
||||||
from optparse import OptionParser
|
|
||||||
import sys
|
|
||||||
|
|
||||||
usage = '''%prog [options] <file prefix>
|
|
||||||
|
|
||||||
File prefix is the path to the main logfile.'''
|
|
||||||
|
|
||||||
parser = OptionParser(usage=usage)
|
|
||||||
parser.add_option(
|
|
||||||
'-p', '--prefix', dest='prefix', default=None, help='Filename prefix.')
|
|
||||||
parser.add_option('-v', '--verbose', dest='verbose',
|
|
||||||
action='store_true', default=False, help='Enable verbose output')
|
|
||||||
(opts, args) = parser.parse_args()
|
|
||||||
|
|
||||||
if opts.prefix is None:
|
|
||||||
print("Please enter a filename prefix")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
try:
|
|
||||||
rotater = LogRotate(prefix=opts.prefix, verbose=opts.verbose)
|
|
||||||
except LogRotateException as e:
|
|
||||||
print(e)
|
|
||||||
sys.exit(1)
|
|
||||||
try:
|
|
||||||
rotater.rotate()
|
|
||||||
except LogRotateException as e:
|
|
||||||
print(e)
|
|
||||||
sys.exit(1)
|
|
||||||
+91
-365
@@ -1,394 +1,120 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
import os
|
||||||
import logging
|
import time
|
||||||
import sys
|
import sys
|
||||||
from typing import Dict, Tuple, Any
|
from tqdm import tqdm
|
||||||
|
import logs.Logger
|
||||||
from db.functions import settype, dbmongo
|
from db.functions import settype, dbmongo
|
||||||
|
from datetime import datetime
|
||||||
|
import json
|
||||||
from db.schema_generator import MongoToPostgresSchemaGenerator, MongoDocumentInserter
|
from db.schema_generator import MongoToPostgresSchemaGenerator, MongoDocumentInserter
|
||||||
import settings.config
|
import settings.config
|
||||||
|
|
||||||
|
def set_apienv(urls, uprocess, dbengine, dbExec, updatesBase, lprint):
|
||||||
# Configuration constants
|
"""Populate updates table from the API and return available updates.
|
||||||
SAMPLE_SIZE = 10000
|
|
||||||
BATCH_SIZE = 10000
|
|
||||||
SCHEMA_NAME = 'updates'
|
|
||||||
PK_FIELD = 'id'
|
|
||||||
|
|
||||||
# Table-to-collection mapping
|
|
||||||
TABLE_CONFIG = {
|
|
||||||
'seriesdata': 'mgseries',
|
|
||||||
'episodesdata': 'mgepisodes',
|
|
||||||
'actorsdata': 'mgactors',
|
|
||||||
'charactersdata': 'mgcharacters',
|
|
||||||
'crewdata': 'mgcrew',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def setup_logger() -> logging.Logger:
|
|
||||||
"""Initialize and return configured logger."""
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
if not logger.handlers:
|
|
||||||
handler = logging.StreamHandler()
|
|
||||||
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
|
||||||
handler.setFormatter(formatter)
|
|
||||||
logger.addHandler(handler)
|
|
||||||
logger.setLevel(logging.INFO)
|
|
||||||
return logger
|
|
||||||
|
|
||||||
|
|
||||||
def initialize_environment(root_dir: str) -> Tuple[Dict[str, Any], Any, Any]:
|
|
||||||
"""Initialize config, database engine, and MongoDB updater.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
root_dir: Root directory path
|
urls: URL configuration with an "updatesurl" key.
|
||||||
|
uprocess: URL loader instance with `get_data`.
|
||||||
|
jget: JSON processor with `jconvert`.
|
||||||
|
dbengine: database engine dict (expects key "engine").
|
||||||
|
dbExec: database execution helper with `update_tvupdates` and `rawsql_select`.
|
||||||
|
updatesBase: list-like containing tables (uses index 8 for updates table).
|
||||||
|
lprint: logger instance with `logprint`.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple of (options dict, db engine, mongo updater)
|
List of tuples (seriesid, timestamp) representing available updates.
|
||||||
"""
|
"""
|
||||||
logger = logging.getLogger(__name__)
|
#updatetable = updatesBase[8]
|
||||||
try:
|
#inputdata = uprocess.get_data(urls["updatesurl"])
|
||||||
config_options = settings.config.Config(root_dir)
|
#lprint.logprint("info", f"Retrieved {len(availableupdates)} rows for processing......")
|
||||||
options = config_options.config_options
|
#
|
||||||
|
newupdates = dbExec.rawsql_select(
|
||||||
if not options:
|
dbengine["engine"],
|
||||||
raise ValueError("Configuration is empty")
|
"select seriesid,timestamp from updates.tvupdates",
|
||||||
|
lprint
|
||||||
dbtype = options.get('dbtype')
|
|
||||||
apitype = options.get('apitype')
|
|
||||||
|
|
||||||
if not dbtype or not apitype:
|
|
||||||
raise ValueError("Missing required config options: dbtype, apitype")
|
|
||||||
|
|
||||||
dbs = settype(dbtype, apitype, options)
|
|
||||||
dbengine = dbs.dbengine
|
|
||||||
mongo_updater = dbmongo(options)
|
|
||||||
|
|
||||||
logger.info("Environment initialized successfully")
|
|
||||||
return options, dbengine, mongo_updater
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to initialize environment: {e}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
def create_tables(
|
|
||||||
generator: MongoToPostgresSchemaGenerator,
|
|
||||||
mongo_updater: Any,
|
|
||||||
dbengine: Dict[str, Any],
|
|
||||||
table_config: Dict[str, str],
|
|
||||||
logger: logging.Logger
|
|
||||||
) -> Dict[str, Dict[str, Any]]:
|
|
||||||
"""Create PostgreSQL tables and return cached schemas.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
generator: Schema generator instance
|
|
||||||
mongo_updater: MongoDB updater instance
|
|
||||||
dbengine: Database engine configuration
|
|
||||||
table_config: Mapping of table names to MongoDB collection attributes
|
|
||||||
logger: Logger instance
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict mapping table names to their schemas
|
|
||||||
"""
|
|
||||||
schemas = {}
|
|
||||||
|
|
||||||
for table_name, collection_attr in table_config.items():
|
|
||||||
try:
|
|
||||||
# Get MongoDB collection
|
|
||||||
collection = getattr(mongo_updater, collection_attr, None)
|
|
||||||
if not collection:
|
|
||||||
logger.warning(f"Collection '{collection_attr}' not found on mongo_updater")
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Analyze schema
|
|
||||||
schema = generator.analyze_collection(mongo_updater, collection)
|
|
||||||
schemas[table_name] = schema
|
|
||||||
|
|
||||||
# Generate and display SQL
|
|
||||||
sql = generator.generate_create_table_sql(
|
|
||||||
table_name,
|
|
||||||
schema_name=SCHEMA_NAME,
|
|
||||||
pk_field=PK_FIELD,
|
|
||||||
)
|
)
|
||||||
logger.info(f"Schema for {table_name}:\n{sql}")
|
return newupdates
|
||||||
|
|
||||||
# Create table in PostgreSQL
|
|
||||||
|
ROOTDIR = os.getcwd()
|
||||||
|
config_options = settings.config.Config(ROOTDIR)
|
||||||
|
options = config_options.config_options
|
||||||
|
dbtype, apitype = options["dbtype"], options["apitype"]
|
||||||
|
dbs = settype(dbtype, apitype, options)
|
||||||
|
dbclass, dbengine = dbs.dbclass, dbs.dbengine
|
||||||
|
mongo_updater = dbmongo(options)
|
||||||
|
generator = MongoToPostgresSchemaGenerator(sample_size=100)
|
||||||
|
|
||||||
|
tableNames = ['seriesdata', 'episodesdata', 'actorsdata', 'charactersdata', 'crewdata']
|
||||||
|
|
||||||
|
for tableName in tableNames:
|
||||||
|
# Generate and print the schema for each collection
|
||||||
|
schema = generator.analyze_collection(mongo_updater, getattr(mongo_updater, f'mg{tableName[:-4]}'))
|
||||||
|
sql = generator.generate_create_table_sql(
|
||||||
|
tableName,
|
||||||
|
schema_name='updates',
|
||||||
|
pk_field='id' if tableName == 'seriesdata' else 'id',
|
||||||
|
)
|
||||||
|
print(f"--- SQL Schema for {tableName} ---")
|
||||||
|
print(sql)
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
# Or create it directly
|
||||||
generator.create_table_in_postgres(
|
generator.create_table_in_postgres(
|
||||||
dbengine,
|
dbengine,
|
||||||
table_name,
|
tableName,
|
||||||
schema_name=SCHEMA_NAME,
|
schema_name='updates',
|
||||||
pk_field=PK_FIELD,
|
#pk_field='seriesid' if tableName == 'seriesdata' else 'id',
|
||||||
|
pk_field='id',
|
||||||
drop_existing=True,
|
drop_existing=True,
|
||||||
)
|
)
|
||||||
logger.info(f"Table '{table_name}' created/recreated successfully")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to create table '{table_name}': {e}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
return schemas
|
|
||||||
|
|
||||||
|
|
||||||
def build_upsert_clause(schema: Dict[str, Any], pk_field: str) -> str:
|
|
||||||
"""Build SQL ON CONFLICT clause for upsert.
|
|
||||||
|
|
||||||
Args:
|
inserter = MongoDocumentInserter(batch_size=1000)
|
||||||
schema: Column schema dictionary
|
|
||||||
pk_field: Primary key field name
|
|
||||||
|
|
||||||
Returns:
|
# Map table names to their MongoDB collections and primary keys
|
||||||
ON CONFLICT clause string
|
collection_map = {
|
||||||
"""
|
'seriesdata': (mongo_updater.mgseries, 'id'),
|
||||||
# Exclude primary key and MongoDB's _id field from updates
|
'episodesdata': (mongo_updater.mgepisodes, 'id'),
|
||||||
update_cols = [
|
'actorsdata': (mongo_updater.mgactors, 'id'),
|
||||||
col.lower() for col in schema.keys()
|
'charactersdata': (mongo_updater.mgcharacters, 'id'),
|
||||||
if col.lower() != pk_field.lower() and col != '_id'
|
'crewdata': (mongo_updater.mgcrew, 'id'),
|
||||||
]
|
}
|
||||||
|
|
||||||
if not update_cols:
|
|
||||||
return f"ON CONFLICT ({pk_field}) DO NOTHING"
|
|
||||||
|
|
||||||
|
# Insert documents from collections with UPSERT
|
||||||
|
for tableName in tableNames:
|
||||||
|
if tableName in collection_map:
|
||||||
|
collection, pk_field = collection_map[tableName]
|
||||||
|
# Build ON CONFLICT ... DO UPDATE clause
|
||||||
|
# Re-analyze the specific collection so we have the correct fields
|
||||||
|
schema = generator.analyze_collection(mongo_updater, collection)
|
||||||
|
# Use lowercased column names to match PostgreSQL unquoted identifiers
|
||||||
|
update_cols = [col.lower() for col in schema.keys() if col.lower() != pk_field.lower() and col != '_id']
|
||||||
update_clause = ', '.join([f"{col}=EXCLUDED.{col}" for col in update_cols])
|
update_clause = ', '.join([f"{col}=EXCLUDED.{col}" for col in update_cols])
|
||||||
return f"ON CONFLICT ({pk_field}) DO UPDATE SET {update_clause}"
|
on_conflict = f"ON CONFLICT ({pk_field}) DO UPDATE SET {update_clause}"
|
||||||
|
|
||||||
|
count = inserter.insert_from_collection(
|
||||||
def should_update_document(
|
engine=dbengine,
|
||||||
doc_id: Any,
|
table_name=tableName,
|
||||||
doc_updated: Any,
|
collection=collection,
|
||||||
dbengine: Dict[str, Any],
|
schema_name='updates',
|
||||||
table_name: str,
|
on_conflict=on_conflict,
|
||||||
schema_name: str,
|
)
|
||||||
logger: logging.Logger
|
print(f"Inserted/Updated {count} documents in {tableName}")
|
||||||
) -> bool:
|
|
||||||
"""Compare document's updated timestamp to the database row's updated timestamp.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
doc_id: Document ID from MongoDB
|
|
||||||
doc_updated: Updated timestamp from MongoDB document
|
|
||||||
dbengine: Database engine configuration
|
|
||||||
table_name: Target table name
|
|
||||||
schema_name: Target schema name
|
|
||||||
logger: Logger instance
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if document should be updated (MongoDB is newer), False otherwise
|
|
||||||
"""
|
|
||||||
if doc_updated is None:
|
|
||||||
logger.debug(f"Document {doc_id} has no 'updated' field, will update")
|
|
||||||
return True
|
|
||||||
|
|
||||||
try:
|
|
||||||
from sqlalchemy import text
|
|
||||||
|
|
||||||
# Query the database for the existing record's updated timestamp
|
|
||||||
query = text(f"""
|
|
||||||
SELECT updated FROM {schema_name}.{table_name}
|
|
||||||
WHERE id = :id
|
|
||||||
""")
|
|
||||||
|
|
||||||
with dbengine['engine'].connect() as conn:
|
|
||||||
result = conn.execute(query, {'id': doc_id}).fetchone()
|
|
||||||
|
|
||||||
# If record doesn't exist, update it
|
|
||||||
if result is None:
|
|
||||||
logger.debug(f"Record {doc_id} not in DB, will insert")
|
|
||||||
return True
|
|
||||||
|
|
||||||
db_updated = result[0]
|
|
||||||
|
|
||||||
# Compare timestamps
|
|
||||||
if db_updated is None:
|
|
||||||
logger.debug(f"Record {doc_id} has NULL updated in DB, will update")
|
|
||||||
return True
|
|
||||||
|
|
||||||
# Only update if MongoDB version is newer
|
|
||||||
should_update = doc_updated > db_updated
|
|
||||||
|
|
||||||
if not should_update:
|
|
||||||
logger.debug(f"Record {doc_id}: DB updated={db_updated} >= doc updated={doc_updated}, skipping")
|
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Record {doc_id}: doc updated={doc_updated} > DB updated={db_updated}, updating")
|
print(f"Warning: No collection mapping for {tableName}")
|
||||||
|
|
||||||
return should_update
|
# # Or directly from MongoDB collection
|
||||||
|
# count = inserter.insert_from_collection(
|
||||||
|
# engine=dbengine,
|
||||||
|
# table_name='seriesdata',
|
||||||
|
# collection=mongo_db['series'],
|
||||||
|
# schema_name='dbo',
|
||||||
|
# on_conflict="DO NOTHING",
|
||||||
|
# )
|
||||||
|
# if __name__ == "__main__":
|
||||||
|
# sys.exit(main())
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Error comparing timestamps for {doc_id}: {e}. Proceeding with update.")
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def insert_documents(
|
|
||||||
inserter: MongoDocumentInserter,
|
|
||||||
mongo_updater: Any,
|
|
||||||
dbengine: Dict[str, Any],
|
|
||||||
table_config: Dict[str, str],
|
|
||||||
schemas: Dict[str, Dict[str, Any]],
|
|
||||||
logger: logging.Logger,
|
|
||||||
compare_timestamps: bool = True
|
|
||||||
) -> None:
|
|
||||||
"""Insert/upsert documents from MongoDB collections to PostgreSQL.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
inserter: Document inserter instance
|
|
||||||
mongo_updater: MongoDB updater instance
|
|
||||||
dbengine: Database engine configuration
|
|
||||||
table_config: Mapping of table names to MongoDB collection attributes
|
|
||||||
schemas: Cached schemas from table creation
|
|
||||||
logger: Logger instance
|
|
||||||
compare_timestamps: If True, only update if MongoDB doc is newer than DB record
|
|
||||||
"""
|
|
||||||
total_inserted = 0
|
|
||||||
total_skipped = 0
|
|
||||||
|
|
||||||
for table_name, collection_attr in table_config.items():
|
|
||||||
if table_name not in schemas:
|
|
||||||
logger.warning(f"Skipping '{table_name}' - schema not available")
|
|
||||||
continue
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Get collection and schema
|
|
||||||
collection = getattr(mongo_updater, collection_attr)
|
|
||||||
schema = schemas[table_name]
|
|
||||||
|
|
||||||
# Build upsert clause
|
|
||||||
on_conflict = build_upsert_clause(schema, PK_FIELD)
|
|
||||||
|
|
||||||
# If timestamp comparison is enabled, filter documents before inserting
|
|
||||||
if compare_timestamps:
|
|
||||||
logger.info(f"Filtering {table_name} documents by updated timestamp and batching every {BATCH_SIZE}...")
|
|
||||||
batch = []
|
|
||||||
batch_num = 0
|
|
||||||
|
|
||||||
for doc in collection.find():
|
|
||||||
doc_id = doc.get(PK_FIELD)
|
|
||||||
doc_updated = doc.get('updated')
|
|
||||||
|
|
||||||
if should_update_document(doc_id, doc_updated, dbengine, table_name, SCHEMA_NAME, logger):
|
|
||||||
batch.append(doc)
|
|
||||||
else:
|
|
||||||
total_skipped += 1
|
|
||||||
|
|
||||||
# When batch reaches size, insert and reset
|
|
||||||
if len(batch) >= BATCH_SIZE:
|
|
||||||
batch_num += 1
|
|
||||||
try:
|
|
||||||
# Explicit transaction per batch
|
|
||||||
with dbengine['engine'].begin():
|
|
||||||
count = inserter.insert_documents(
|
|
||||||
engine=dbengine,
|
|
||||||
table_name=table_name,
|
|
||||||
documents=batch,
|
|
||||||
schema_name=SCHEMA_NAME,
|
|
||||||
on_conflict=on_conflict,
|
|
||||||
)
|
|
||||||
total_inserted += count
|
|
||||||
logger.info(f"Batch {batch_num}: Inserted/Updated {count} documents into '{table_name}' (committed)")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Batch {batch_num} failed for '{table_name}': {e}")
|
|
||||||
finally:
|
|
||||||
batch = []
|
|
||||||
|
|
||||||
# Insert any remaining docs in the final batch
|
|
||||||
if batch:
|
|
||||||
batch_num += 1
|
|
||||||
try:
|
|
||||||
with dbengine['engine'].begin():
|
|
||||||
count = inserter.insert_documents(
|
|
||||||
engine=dbengine,
|
|
||||||
table_name=table_name,
|
|
||||||
documents=batch,
|
|
||||||
schema_name=SCHEMA_NAME,
|
|
||||||
on_conflict=on_conflict,
|
|
||||||
)
|
|
||||||
total_inserted += count
|
|
||||||
logger.info(f"Final batch {batch_num}: Inserted/Updated {count} documents into '{table_name}' (committed)")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Final batch {batch_num} failed for '{table_name}': {e}")
|
|
||||||
finally:
|
|
||||||
batch = []
|
|
||||||
else:
|
|
||||||
# Insert all documents without timestamp comparison
|
|
||||||
# Stream and batch inserts to avoid memory spikes
|
|
||||||
batch = []
|
|
||||||
batch_num = 0
|
|
||||||
for doc in collection.find():
|
|
||||||
batch.append(doc)
|
|
||||||
if len(batch) >= BATCH_SIZE:
|
|
||||||
batch_num += 1
|
|
||||||
try:
|
|
||||||
with dbengine['engine'].begin():
|
|
||||||
count = inserter.insert_documents(
|
|
||||||
engine=dbengine,
|
|
||||||
table_name=table_name,
|
|
||||||
documents=batch,
|
|
||||||
schema_name=SCHEMA_NAME,
|
|
||||||
on_conflict=on_conflict,
|
|
||||||
)
|
|
||||||
total_inserted += count
|
|
||||||
logger.info(f"Batch {batch_num}: Inserted/Updated {count} documents into '{table_name}' (committed)")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Batch {batch_num} failed for '{table_name}': {e}")
|
|
||||||
finally:
|
|
||||||
batch = []
|
|
||||||
|
|
||||||
if batch:
|
|
||||||
batch_num += 1
|
|
||||||
try:
|
|
||||||
with dbengine['engine'].begin():
|
|
||||||
count = inserter.insert_documents(
|
|
||||||
engine=dbengine,
|
|
||||||
table_name=table_name,
|
|
||||||
documents=batch,
|
|
||||||
schema_name=SCHEMA_NAME,
|
|
||||||
on_conflict=on_conflict,
|
|
||||||
)
|
|
||||||
total_inserted += count
|
|
||||||
logger.info(f"Final batch {batch_num}: Inserted/Updated {count} documents into '{table_name}' (committed)")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Final batch {batch_num} failed for '{table_name}': {e}")
|
|
||||||
finally:
|
|
||||||
batch = []
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to insert documents into '{table_name}': {e}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
logger.info(f"Total documents inserted/updated: {total_inserted}, skipped: {total_skipped}")
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
"""Main entry point for MongoDB to PostgreSQL sync."""
|
|
||||||
logger = setup_logger()
|
|
||||||
|
|
||||||
try:
|
|
||||||
root_dir = os.getcwd()
|
|
||||||
|
|
||||||
# Initialize environment
|
|
||||||
options, dbengine, mongo_updater = initialize_environment(root_dir)
|
|
||||||
|
|
||||||
# Create schema generator and inserter
|
|
||||||
generator = MongoToPostgresSchemaGenerator(sample_size=SAMPLE_SIZE)
|
|
||||||
inserter = MongoDocumentInserter(batch_size=BATCH_SIZE)
|
|
||||||
|
|
||||||
# Create tables and get cached schemas
|
|
||||||
logger.info("Creating PostgreSQL tables...")
|
|
||||||
schemas = create_tables(generator, mongo_updater, dbengine, TABLE_CONFIG, logger)
|
|
||||||
|
|
||||||
if not schemas:
|
|
||||||
logger.error("No schemas were successfully created. Aborting insert.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# Insert documents from MongoDB
|
|
||||||
logger.info("Inserting documents from MongoDB...")
|
|
||||||
insert_documents(inserter, mongo_updater, dbengine, TABLE_CONFIG, schemas, logger)
|
|
||||||
|
|
||||||
logger.info("MongoDB to PostgreSQL sync completed successfully")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Sync failed: {e}", exc_info=True)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ loglevel=info
|
|||||||
sqlserver_driver=ODBC Driver 13 for SQL Server
|
sqlserver_driver=ODBC Driver 13 for SQL Server
|
||||||
hostname=192.168.128.3
|
hostname=192.168.128.3
|
||||||
mghostname=192.168.128.24
|
mghostname=192.168.128.24
|
||||||
dbname=media_dbsync
|
dbname=media_dbsync2
|
||||||
mgdbname=tvdata
|
mgdbname=tvdata
|
||||||
mysqlusername=root
|
mysqlusername=root
|
||||||
pgsqlusername=postgres
|
pgsqlusername=postgres
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
[global]
|
|
||||||
loglevel=info
|
|
||||||
|
|
||||||
[dbsettings]
|
|
||||||
sqlserver_driver=ODBC Driver 13 for SQL Server
|
|
||||||
hostname=192.168.128.3
|
|
||||||
mghostname=192.168.128.24
|
|
||||||
dbname=media_dbsync
|
|
||||||
mgdbname=tvdata
|
|
||||||
mysqlusername=root
|
|
||||||
pgsqlusername=postgres
|
|
||||||
mssqlusername=sa
|
|
||||||
mysqlpassword=Optimus0329
|
|
||||||
pgsqlpassword=Optimus0329
|
|
||||||
mssqlpassword=Optimus0329
|
|
||||||
pgsqlport=5432
|
|
||||||
mysqlport=3306
|
|
||||||
mssqlport=1433
|
|
||||||
mgport=27017
|
|
||||||
|
|
||||||
[api]
|
|
||||||
type=tvmaze
|
|
||||||
|
|
||||||
[json]
|
|
||||||
seriesapi=http://api.tvmaze.com/shows?page=<pagenum>
|
|
||||||
showapi=http://api.tvmaze.com/shows/<tvmazeid>
|
|
||||||
episodeapi=http://api.tvmaze.com/shows/<tvmazeid>/episodes
|
|
||||||
aliasapi=http://api.tvmaze.com/shows/<tvmazeid>/akas
|
|
||||||
castapi=http://api.tvmaze.com/shows/<tvmazeid>/cast
|
|
||||||
crewapi=http://api.tvmaze.com/shows/<tvmazeid>/crew
|
|
||||||
creditsapi=http://api.tvmaze.com/people/<actorid>/castcredits
|
|
||||||
updatesapi=http://api.tvmaze.com/updates/shows
|
|
||||||
|
|
||||||
[database]
|
|
||||||
dbtype=pgsql
|
|
||||||
updateschema=updates
|
|
||||||
initialize=0
|
|
||||||
initload=0
|
|
||||||
|
|
||||||
[updatetype]
|
|
||||||
#valid values are: daily, weekly, monthly, full, custom
|
|
||||||
type=full
|
|
||||||
full=full
|
|
||||||
monthly=1219200
|
|
||||||
weekly=604800
|
|
||||||
daily=86400
|
|
||||||
12h=23200
|
|
||||||
6h = 16600
|
|
||||||
# If settype = custom set below line to the number of seconds you want updates for.
|
|
||||||
custom=4150
|
|
||||||
cacheskip = 1
|
|
||||||
refreshcache = 0
|
|
||||||
|
|
||||||
# Field names for series insert query
|
|
||||||
# field01: nextepisode,
|
|
||||||
# field02: previousepisode,
|
|
||||||
# field03: mazeurl,
|
|
||||||
# field04: tvdbid,
|
|
||||||
# field05: tvrageid,
|
|
||||||
# field06: genres,
|
|
||||||
# field07: tvmazeid,
|
|
||||||
# field08: mediumimage,
|
|
||||||
# field09: originalimage,
|
|
||||||
# field10: showlanguage,
|
|
||||||
# field11: seriesname,
|
|
||||||
# field12: webchannelcountrycode,
|
|
||||||
# field13: webchannelcountryname,
|
|
||||||
# field14: webchannelcountrytimezone,
|
|
||||||
# field15: webchannelid,
|
|
||||||
# field16: webchannelname,
|
|
||||||
# field17: networkcountrycode,
|
|
||||||
# field18: networkcountryname,
|
|
||||||
# field19: networkcountrytimezone,
|
|
||||||
# field20: networkid,
|
|
||||||
# field21: networkname,
|
|
||||||
# field22: premierdate,
|
|
||||||
# field23: showrating,
|
|
||||||
# field24: showruntime,
|
|
||||||
# field25: airdays,
|
|
||||||
# field26: scheduletime,
|
|
||||||
# field27: showstatus,
|
|
||||||
# field28: showoverview,
|
|
||||||
# field29: showtype,
|
|
||||||
# field30: showupdated,
|
|
||||||
# field31: apilink,
|
|
||||||
# field32: showweight
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,986 +0,0 @@
|
|||||||
137;http://api.tvmaze.com/people/137/castcredits
|
|
||||||
145;http://api.tvmaze.com/people/145/castcredits
|
|
||||||
137;http://api.tvmaze.com/people/137/castcredits
|
|
||||||
145;http://api.tvmaze.com/people/145/castcredits
|
|
||||||
211;http://api.tvmaze.com/people/211/castcredits
|
|
||||||
214;http://api.tvmaze.com/people/214/castcredits
|
|
||||||
315;http://api.tvmaze.com/people/315/castcredits
|
|
||||||
368;http://api.tvmaze.com/people/368/castcredits
|
|
||||||
416;http://api.tvmaze.com/people/416/castcredits
|
|
||||||
473;http://api.tvmaze.com/people/473/castcredits
|
|
||||||
557;http://api.tvmaze.com/people/557/castcredits
|
|
||||||
577;http://api.tvmaze.com/people/577/castcredits
|
|
||||||
583;http://api.tvmaze.com/people/583/castcredits
|
|
||||||
635;http://api.tvmaze.com/people/635/castcredits
|
|
||||||
688;http://api.tvmaze.com/people/688/castcredits
|
|
||||||
743;http://api.tvmaze.com/people/743/castcredits
|
|
||||||
771;http://api.tvmaze.com/people/771/castcredits
|
|
||||||
811;http://api.tvmaze.com/people/811/castcredits
|
|
||||||
817;http://api.tvmaze.com/people/817/castcredits
|
|
||||||
823;http://api.tvmaze.com/people/823/castcredits
|
|
||||||
864;http://api.tvmaze.com/people/864/castcredits
|
|
||||||
1008;http://api.tvmaze.com/people/1008/castcredits
|
|
||||||
1037;http://api.tvmaze.com/people/1037/castcredits
|
|
||||||
1100;http://api.tvmaze.com/people/1100/castcredits
|
|
||||||
1238;http://api.tvmaze.com/people/1238/castcredits
|
|
||||||
1267;http://api.tvmaze.com/people/1267/castcredits
|
|
||||||
1314;http://api.tvmaze.com/people/1314/castcredits
|
|
||||||
1358;http://api.tvmaze.com/people/1358/castcredits
|
|
||||||
1375;http://api.tvmaze.com/people/1375/castcredits
|
|
||||||
1394;http://api.tvmaze.com/people/1394/castcredits
|
|
||||||
1555;http://api.tvmaze.com/people/1555/castcredits
|
|
||||||
1611;http://api.tvmaze.com/people/1611/castcredits
|
|
||||||
1749;http://api.tvmaze.com/people/1749/castcredits
|
|
||||||
1813;http://api.tvmaze.com/people/1813/castcredits
|
|
||||||
2002;http://api.tvmaze.com/people/2002/castcredits
|
|
||||||
2041;http://api.tvmaze.com/people/2041/castcredits
|
|
||||||
2101;http://api.tvmaze.com/people/2101/castcredits
|
|
||||||
2191;http://api.tvmaze.com/people/2191/castcredits
|
|
||||||
2233;http://api.tvmaze.com/people/2233/castcredits
|
|
||||||
2280;http://api.tvmaze.com/people/2280/castcredits
|
|
||||||
2292;http://api.tvmaze.com/people/2292/castcredits
|
|
||||||
2307;http://api.tvmaze.com/people/2307/castcredits
|
|
||||||
2339;http://api.tvmaze.com/people/2339/castcredits
|
|
||||||
2342;http://api.tvmaze.com/people/2342/castcredits
|
|
||||||
2408;http://api.tvmaze.com/people/2408/castcredits
|
|
||||||
2428;http://api.tvmaze.com/people/2428/castcredits
|
|
||||||
2466;http://api.tvmaze.com/people/2466/castcredits
|
|
||||||
2542;http://api.tvmaze.com/people/2542/castcredits
|
|
||||||
2545;http://api.tvmaze.com/people/2545/castcredits
|
|
||||||
2654;http://api.tvmaze.com/people/2654/castcredits
|
|
||||||
2692;http://api.tvmaze.com/people/2692/castcredits
|
|
||||||
2735;http://api.tvmaze.com/people/2735/castcredits
|
|
||||||
2736;http://api.tvmaze.com/people/2736/castcredits
|
|
||||||
2737;http://api.tvmaze.com/people/2737/castcredits
|
|
||||||
2743;http://api.tvmaze.com/people/2743/castcredits
|
|
||||||
2745;http://api.tvmaze.com/people/2745/castcredits
|
|
||||||
2747;http://api.tvmaze.com/people/2747/castcredits
|
|
||||||
2963;http://api.tvmaze.com/people/2963/castcredits
|
|
||||||
2972;http://api.tvmaze.com/people/2972/castcredits
|
|
||||||
3020;http://api.tvmaze.com/people/3020/castcredits
|
|
||||||
3032;http://api.tvmaze.com/people/3032/castcredits
|
|
||||||
3046;http://api.tvmaze.com/people/3046/castcredits
|
|
||||||
3092;http://api.tvmaze.com/people/3092/castcredits
|
|
||||||
3261;http://api.tvmaze.com/people/3261/castcredits
|
|
||||||
3302;http://api.tvmaze.com/people/3302/castcredits
|
|
||||||
3346;http://api.tvmaze.com/people/3346/castcredits
|
|
||||||
3428;http://api.tvmaze.com/people/3428/castcredits
|
|
||||||
3454;http://api.tvmaze.com/people/3454/castcredits
|
|
||||||
3464;http://api.tvmaze.com/people/3464/castcredits
|
|
||||||
3505;http://api.tvmaze.com/people/3505/castcredits
|
|
||||||
3581;http://api.tvmaze.com/people/3581/castcredits
|
|
||||||
3777;http://api.tvmaze.com/people/3777/castcredits
|
|
||||||
3841;http://api.tvmaze.com/people/3841/castcredits
|
|
||||||
3878;http://api.tvmaze.com/people/3878/castcredits
|
|
||||||
3909;http://api.tvmaze.com/people/3909/castcredits
|
|
||||||
3942;http://api.tvmaze.com/people/3942/castcredits
|
|
||||||
3944;http://api.tvmaze.com/people/3944/castcredits
|
|
||||||
3946;http://api.tvmaze.com/people/3946/castcredits
|
|
||||||
3948;http://api.tvmaze.com/people/3948/castcredits
|
|
||||||
3950;http://api.tvmaze.com/people/3950/castcredits
|
|
||||||
3952;http://api.tvmaze.com/people/3952/castcredits
|
|
||||||
3954;http://api.tvmaze.com/people/3954/castcredits
|
|
||||||
3965;http://api.tvmaze.com/people/3965/castcredits
|
|
||||||
4046;http://api.tvmaze.com/people/4046/castcredits
|
|
||||||
4193;http://api.tvmaze.com/people/4193/castcredits
|
|
||||||
4261;http://api.tvmaze.com/people/4261/castcredits
|
|
||||||
4276;http://api.tvmaze.com/people/4276/castcredits
|
|
||||||
4379;http://api.tvmaze.com/people/4379/castcredits
|
|
||||||
4395;http://api.tvmaze.com/people/4395/castcredits
|
|
||||||
4406;http://api.tvmaze.com/people/4406/castcredits
|
|
||||||
4445;http://api.tvmaze.com/people/4445/castcredits
|
|
||||||
4492;http://api.tvmaze.com/people/4492/castcredits
|
|
||||||
4548;http://api.tvmaze.com/people/4548/castcredits
|
|
||||||
4585;http://api.tvmaze.com/people/4585/castcredits
|
|
||||||
4601;http://api.tvmaze.com/people/4601/castcredits
|
|
||||||
4677;http://api.tvmaze.com/people/4677/castcredits
|
|
||||||
4820;http://api.tvmaze.com/people/4820/castcredits
|
|
||||||
4841;http://api.tvmaze.com/people/4841/castcredits
|
|
||||||
4851;http://api.tvmaze.com/people/4851/castcredits
|
|
||||||
4908;http://api.tvmaze.com/people/4908/castcredits
|
|
||||||
4911;http://api.tvmaze.com/people/4911/castcredits
|
|
||||||
4941;http://api.tvmaze.com/people/4941/castcredits
|
|
||||||
4983;http://api.tvmaze.com/people/4983/castcredits
|
|
||||||
5010;http://api.tvmaze.com/people/5010/castcredits
|
|
||||||
5013;http://api.tvmaze.com/people/5013/castcredits
|
|
||||||
5019;http://api.tvmaze.com/people/5019/castcredits
|
|
||||||
5051;http://api.tvmaze.com/people/5051/castcredits
|
|
||||||
5066;http://api.tvmaze.com/people/5066/castcredits
|
|
||||||
5198;http://api.tvmaze.com/people/5198/castcredits
|
|
||||||
5205;http://api.tvmaze.com/people/5205/castcredits
|
|
||||||
5208;http://api.tvmaze.com/people/5208/castcredits
|
|
||||||
5209;http://api.tvmaze.com/people/5209/castcredits
|
|
||||||
5210;http://api.tvmaze.com/people/5210/castcredits
|
|
||||||
5211;http://api.tvmaze.com/people/5211/castcredits
|
|
||||||
5212;http://api.tvmaze.com/people/5212/castcredits
|
|
||||||
5233;http://api.tvmaze.com/people/5233/castcredits
|
|
||||||
5277;http://api.tvmaze.com/people/5277/castcredits
|
|
||||||
5425;http://api.tvmaze.com/people/5425/castcredits
|
|
||||||
5450;http://api.tvmaze.com/people/5450/castcredits
|
|
||||||
5584;http://api.tvmaze.com/people/5584/castcredits
|
|
||||||
5615;http://api.tvmaze.com/people/5615/castcredits
|
|
||||||
5622;http://api.tvmaze.com/people/5622/castcredits
|
|
||||||
5758;http://api.tvmaze.com/people/5758/castcredits
|
|
||||||
5760;http://api.tvmaze.com/people/5760/castcredits
|
|
||||||
5777;http://api.tvmaze.com/people/5777/castcredits
|
|
||||||
5885;http://api.tvmaze.com/people/5885/castcredits
|
|
||||||
5934;http://api.tvmaze.com/people/5934/castcredits
|
|
||||||
5936;http://api.tvmaze.com/people/5936/castcredits
|
|
||||||
5950;http://api.tvmaze.com/people/5950/castcredits
|
|
||||||
5952;http://api.tvmaze.com/people/5952/castcredits
|
|
||||||
5954;http://api.tvmaze.com/people/5954/castcredits
|
|
||||||
5992;http://api.tvmaze.com/people/5992/castcredits
|
|
||||||
5994;http://api.tvmaze.com/people/5994/castcredits
|
|
||||||
5996;http://api.tvmaze.com/people/5996/castcredits
|
|
||||||
5998;http://api.tvmaze.com/people/5998/castcredits
|
|
||||||
6000;http://api.tvmaze.com/people/6000/castcredits
|
|
||||||
6002;http://api.tvmaze.com/people/6002/castcredits
|
|
||||||
6004;http://api.tvmaze.com/people/6004/castcredits
|
|
||||||
6006;http://api.tvmaze.com/people/6006/castcredits
|
|
||||||
6008;http://api.tvmaze.com/people/6008/castcredits
|
|
||||||
6010;http://api.tvmaze.com/people/6010/castcredits
|
|
||||||
6012;http://api.tvmaze.com/people/6012/castcredits
|
|
||||||
6014;http://api.tvmaze.com/people/6014/castcredits
|
|
||||||
6016;http://api.tvmaze.com/people/6016/castcredits
|
|
||||||
6018;http://api.tvmaze.com/people/6018/castcredits
|
|
||||||
6071;http://api.tvmaze.com/people/6071/castcredits
|
|
||||||
6130;http://api.tvmaze.com/people/6130/castcredits
|
|
||||||
6158;http://api.tvmaze.com/people/6158/castcredits
|
|
||||||
6185;http://api.tvmaze.com/people/6185/castcredits
|
|
||||||
6197;http://api.tvmaze.com/people/6197/castcredits
|
|
||||||
6199;http://api.tvmaze.com/people/6199/castcredits
|
|
||||||
6201;http://api.tvmaze.com/people/6201/castcredits
|
|
||||||
6203;http://api.tvmaze.com/people/6203/castcredits
|
|
||||||
6205;http://api.tvmaze.com/people/6205/castcredits
|
|
||||||
6207;http://api.tvmaze.com/people/6207/castcredits
|
|
||||||
6463;http://api.tvmaze.com/people/6463/castcredits
|
|
||||||
6471;http://api.tvmaze.com/people/6471/castcredits
|
|
||||||
6642;http://api.tvmaze.com/people/6642/castcredits
|
|
||||||
6693;http://api.tvmaze.com/people/6693/castcredits
|
|
||||||
6719;http://api.tvmaze.com/people/6719/castcredits
|
|
||||||
6741;http://api.tvmaze.com/people/6741/castcredits
|
|
||||||
6801;http://api.tvmaze.com/people/6801/castcredits
|
|
||||||
6943;http://api.tvmaze.com/people/6943/castcredits
|
|
||||||
7063;http://api.tvmaze.com/people/7063/castcredits
|
|
||||||
7251;http://api.tvmaze.com/people/7251/castcredits
|
|
||||||
7282;http://api.tvmaze.com/people/7282/castcredits
|
|
||||||
7320;http://api.tvmaze.com/people/7320/castcredits
|
|
||||||
7574;http://api.tvmaze.com/people/7574/castcredits
|
|
||||||
7591;http://api.tvmaze.com/people/7591/castcredits
|
|
||||||
7686;http://api.tvmaze.com/people/7686/castcredits
|
|
||||||
7730;http://api.tvmaze.com/people/7730/castcredits
|
|
||||||
7740;http://api.tvmaze.com/people/7740/castcredits
|
|
||||||
7815;http://api.tvmaze.com/people/7815/castcredits
|
|
||||||
7834;http://api.tvmaze.com/people/7834/castcredits
|
|
||||||
7848;http://api.tvmaze.com/people/7848/castcredits
|
|
||||||
7876;http://api.tvmaze.com/people/7876/castcredits
|
|
||||||
7950;http://api.tvmaze.com/people/7950/castcredits
|
|
||||||
8071;http://api.tvmaze.com/people/8071/castcredits
|
|
||||||
8081;http://api.tvmaze.com/people/8081/castcredits
|
|
||||||
8089;http://api.tvmaze.com/people/8089/castcredits
|
|
||||||
8171;http://api.tvmaze.com/people/8171/castcredits
|
|
||||||
8218;http://api.tvmaze.com/people/8218/castcredits
|
|
||||||
8382;http://api.tvmaze.com/people/8382/castcredits
|
|
||||||
8450;http://api.tvmaze.com/people/8450/castcredits
|
|
||||||
8497;http://api.tvmaze.com/people/8497/castcredits
|
|
||||||
8524;http://api.tvmaze.com/people/8524/castcredits
|
|
||||||
8525;http://api.tvmaze.com/people/8525/castcredits
|
|
||||||
8527;http://api.tvmaze.com/people/8527/castcredits
|
|
||||||
8560;http://api.tvmaze.com/people/8560/castcredits
|
|
||||||
8574;http://api.tvmaze.com/people/8574/castcredits
|
|
||||||
8581;http://api.tvmaze.com/people/8581/castcredits
|
|
||||||
8585;http://api.tvmaze.com/people/8585/castcredits
|
|
||||||
8587;http://api.tvmaze.com/people/8587/castcredits
|
|
||||||
8589;http://api.tvmaze.com/people/8589/castcredits
|
|
||||||
8590;http://api.tvmaze.com/people/8590/castcredits
|
|
||||||
8591;http://api.tvmaze.com/people/8591/castcredits
|
|
||||||
8593;http://api.tvmaze.com/people/8593/castcredits
|
|
||||||
8692;http://api.tvmaze.com/people/8692/castcredits
|
|
||||||
8747;http://api.tvmaze.com/people/8747/castcredits
|
|
||||||
8752;http://api.tvmaze.com/people/8752/castcredits
|
|
||||||
8789;http://api.tvmaze.com/people/8789/castcredits
|
|
||||||
8848;http://api.tvmaze.com/people/8848/castcredits
|
|
||||||
9010;http://api.tvmaze.com/people/9010/castcredits
|
|
||||||
9037;http://api.tvmaze.com/people/9037/castcredits
|
|
||||||
9162;http://api.tvmaze.com/people/9162/castcredits
|
|
||||||
9212;http://api.tvmaze.com/people/9212/castcredits
|
|
||||||
9217;http://api.tvmaze.com/people/9217/castcredits
|
|
||||||
9219;http://api.tvmaze.com/people/9219/castcredits
|
|
||||||
9221;http://api.tvmaze.com/people/9221/castcredits
|
|
||||||
9223;http://api.tvmaze.com/people/9223/castcredits
|
|
||||||
9225;http://api.tvmaze.com/people/9225/castcredits
|
|
||||||
9237;http://api.tvmaze.com/people/9237/castcredits
|
|
||||||
9286;http://api.tvmaze.com/people/9286/castcredits
|
|
||||||
9317;http://api.tvmaze.com/people/9317/castcredits
|
|
||||||
9320;http://api.tvmaze.com/people/9320/castcredits
|
|
||||||
9321;http://api.tvmaze.com/people/9321/castcredits
|
|
||||||
9322;http://api.tvmaze.com/people/9322/castcredits
|
|
||||||
9323;http://api.tvmaze.com/people/9323/castcredits
|
|
||||||
9324;http://api.tvmaze.com/people/9324/castcredits
|
|
||||||
9325;http://api.tvmaze.com/people/9325/castcredits
|
|
||||||
9326;http://api.tvmaze.com/people/9326/castcredits
|
|
||||||
9327;http://api.tvmaze.com/people/9327/castcredits
|
|
||||||
9328;http://api.tvmaze.com/people/9328/castcredits
|
|
||||||
9329;http://api.tvmaze.com/people/9329/castcredits
|
|
||||||
9330;http://api.tvmaze.com/people/9330/castcredits
|
|
||||||
9331;http://api.tvmaze.com/people/9331/castcredits
|
|
||||||
9332;http://api.tvmaze.com/people/9332/castcredits
|
|
||||||
9334;http://api.tvmaze.com/people/9334/castcredits
|
|
||||||
9335;http://api.tvmaze.com/people/9335/castcredits
|
|
||||||
9336;http://api.tvmaze.com/people/9336/castcredits
|
|
||||||
9355;http://api.tvmaze.com/people/9355/castcredits
|
|
||||||
9666;http://api.tvmaze.com/people/9666/castcredits
|
|
||||||
9754;http://api.tvmaze.com/people/9754/castcredits
|
|
||||||
9772;http://api.tvmaze.com/people/9772/castcredits
|
|
||||||
9801;http://api.tvmaze.com/people/9801/castcredits
|
|
||||||
9851;http://api.tvmaze.com/people/9851/castcredits
|
|
||||||
9891;http://api.tvmaze.com/people/9891/castcredits
|
|
||||||
9913;http://api.tvmaze.com/people/9913/castcredits
|
|
||||||
9929;http://api.tvmaze.com/people/9929/castcredits
|
|
||||||
9931;http://api.tvmaze.com/people/9931/castcredits
|
|
||||||
9933;http://api.tvmaze.com/people/9933/castcredits
|
|
||||||
9935;http://api.tvmaze.com/people/9935/castcredits
|
|
||||||
9937;http://api.tvmaze.com/people/9937/castcredits
|
|
||||||
9939;http://api.tvmaze.com/people/9939/castcredits
|
|
||||||
9948;http://api.tvmaze.com/people/9948/castcredits
|
|
||||||
9971;http://api.tvmaze.com/people/9971/castcredits
|
|
||||||
10118;http://api.tvmaze.com/people/10118/castcredits
|
|
||||||
10119;http://api.tvmaze.com/people/10119/castcredits
|
|
||||||
10120;http://api.tvmaze.com/people/10120/castcredits
|
|
||||||
10121;http://api.tvmaze.com/people/10121/castcredits
|
|
||||||
10122;http://api.tvmaze.com/people/10122/castcredits
|
|
||||||
10123;http://api.tvmaze.com/people/10123/castcredits
|
|
||||||
10124;http://api.tvmaze.com/people/10124/castcredits
|
|
||||||
10125;http://api.tvmaze.com/people/10125/castcredits
|
|
||||||
10126;http://api.tvmaze.com/people/10126/castcredits
|
|
||||||
10127;http://api.tvmaze.com/people/10127/castcredits
|
|
||||||
137;http://api.tvmaze.com/people/137/castcredits
|
|
||||||
145;http://api.tvmaze.com/people/145/castcredits
|
|
||||||
211;http://api.tvmaze.com/people/211/castcredits
|
|
||||||
214;http://api.tvmaze.com/people/214/castcredits
|
|
||||||
315;http://api.tvmaze.com/people/315/castcredits
|
|
||||||
368;http://api.tvmaze.com/people/368/castcredits
|
|
||||||
416;http://api.tvmaze.com/people/416/castcredits
|
|
||||||
473;http://api.tvmaze.com/people/473/castcredits
|
|
||||||
557;http://api.tvmaze.com/people/557/castcredits
|
|
||||||
577;http://api.tvmaze.com/people/577/castcredits
|
|
||||||
583;http://api.tvmaze.com/people/583/castcredits
|
|
||||||
635;http://api.tvmaze.com/people/635/castcredits
|
|
||||||
688;http://api.tvmaze.com/people/688/castcredits
|
|
||||||
743;http://api.tvmaze.com/people/743/castcredits
|
|
||||||
771;http://api.tvmaze.com/people/771/castcredits
|
|
||||||
10148;http://api.tvmaze.com/people/10148/castcredits
|
|
||||||
10149;http://api.tvmaze.com/people/10149/castcredits
|
|
||||||
10162;http://api.tvmaze.com/people/10162/castcredits
|
|
||||||
10177;http://api.tvmaze.com/people/10177/castcredits
|
|
||||||
10213;http://api.tvmaze.com/people/10213/castcredits
|
|
||||||
10271;http://api.tvmaze.com/people/10271/castcredits
|
|
||||||
10283;http://api.tvmaze.com/people/10283/castcredits
|
|
||||||
10309;http://api.tvmaze.com/people/10309/castcredits
|
|
||||||
10333;http://api.tvmaze.com/people/10333/castcredits
|
|
||||||
10355;http://api.tvmaze.com/people/10355/castcredits
|
|
||||||
10383;http://api.tvmaze.com/people/10383/castcredits
|
|
||||||
10390;http://api.tvmaze.com/people/10390/castcredits
|
|
||||||
10455;http://api.tvmaze.com/people/10455/castcredits
|
|
||||||
10462;http://api.tvmaze.com/people/10462/castcredits
|
|
||||||
10477;http://api.tvmaze.com/people/10477/castcredits
|
|
||||||
10513;http://api.tvmaze.com/people/10513/castcredits
|
|
||||||
10529;http://api.tvmaze.com/people/10529/castcredits
|
|
||||||
10532;http://api.tvmaze.com/people/10532/castcredits
|
|
||||||
10534;http://api.tvmaze.com/people/10534/castcredits
|
|
||||||
10616;http://api.tvmaze.com/people/10616/castcredits
|
|
||||||
10647;http://api.tvmaze.com/people/10647/castcredits
|
|
||||||
10659;http://api.tvmaze.com/people/10659/castcredits
|
|
||||||
10720;http://api.tvmaze.com/people/10720/castcredits
|
|
||||||
10862;http://api.tvmaze.com/people/10862/castcredits
|
|
||||||
10864;http://api.tvmaze.com/people/10864/castcredits
|
|
||||||
10865;http://api.tvmaze.com/people/10865/castcredits
|
|
||||||
10866;http://api.tvmaze.com/people/10866/castcredits
|
|
||||||
10867;http://api.tvmaze.com/people/10867/castcredits
|
|
||||||
10868;http://api.tvmaze.com/people/10868/castcredits
|
|
||||||
10970;http://api.tvmaze.com/people/10970/castcredits
|
|
||||||
11039;http://api.tvmaze.com/people/11039/castcredits
|
|
||||||
11047;http://api.tvmaze.com/people/11047/castcredits
|
|
||||||
11079;http://api.tvmaze.com/people/11079/castcredits
|
|
||||||
11150;http://api.tvmaze.com/people/11150/castcredits
|
|
||||||
11178;http://api.tvmaze.com/people/11178/castcredits
|
|
||||||
11312;http://api.tvmaze.com/people/11312/castcredits
|
|
||||||
11402;http://api.tvmaze.com/people/11402/castcredits
|
|
||||||
11453;http://api.tvmaze.com/people/11453/castcredits
|
|
||||||
11457;http://api.tvmaze.com/people/11457/castcredits
|
|
||||||
11493;http://api.tvmaze.com/people/11493/castcredits
|
|
||||||
11546;http://api.tvmaze.com/people/11546/castcredits
|
|
||||||
11547;http://api.tvmaze.com/people/11547/castcredits
|
|
||||||
11548;http://api.tvmaze.com/people/11548/castcredits
|
|
||||||
11549;http://api.tvmaze.com/people/11549/castcredits
|
|
||||||
11550;http://api.tvmaze.com/people/11550/castcredits
|
|
||||||
11551;http://api.tvmaze.com/people/11551/castcredits
|
|
||||||
11593;http://api.tvmaze.com/people/11593/castcredits
|
|
||||||
11621;http://api.tvmaze.com/people/11621/castcredits
|
|
||||||
11759;http://api.tvmaze.com/people/11759/castcredits
|
|
||||||
11790;http://api.tvmaze.com/people/11790/castcredits
|
|
||||||
11952;http://api.tvmaze.com/people/11952/castcredits
|
|
||||||
11971;http://api.tvmaze.com/people/11971/castcredits
|
|
||||||
11989;http://api.tvmaze.com/people/11989/castcredits
|
|
||||||
12043;http://api.tvmaze.com/people/12043/castcredits
|
|
||||||
12066;http://api.tvmaze.com/people/12066/castcredits
|
|
||||||
12096;http://api.tvmaze.com/people/12096/castcredits
|
|
||||||
12097;http://api.tvmaze.com/people/12097/castcredits
|
|
||||||
12098;http://api.tvmaze.com/people/12098/castcredits
|
|
||||||
12099;http://api.tvmaze.com/people/12099/castcredits
|
|
||||||
12100;http://api.tvmaze.com/people/12100/castcredits
|
|
||||||
12101;http://api.tvmaze.com/people/12101/castcredits
|
|
||||||
12102;http://api.tvmaze.com/people/12102/castcredits
|
|
||||||
12141;http://api.tvmaze.com/people/12141/castcredits
|
|
||||||
12146;http://api.tvmaze.com/people/12146/castcredits
|
|
||||||
12147;http://api.tvmaze.com/people/12147/castcredits
|
|
||||||
12148;http://api.tvmaze.com/people/12148/castcredits
|
|
||||||
12149;http://api.tvmaze.com/people/12149/castcredits
|
|
||||||
12150;http://api.tvmaze.com/people/12150/castcredits
|
|
||||||
12201;http://api.tvmaze.com/people/12201/castcredits
|
|
||||||
12379;http://api.tvmaze.com/people/12379/castcredits
|
|
||||||
12445;http://api.tvmaze.com/people/12445/castcredits
|
|
||||||
12575;http://api.tvmaze.com/people/12575/castcredits
|
|
||||||
12650;http://api.tvmaze.com/people/12650/castcredits
|
|
||||||
12666;http://api.tvmaze.com/people/12666/castcredits
|
|
||||||
12842;http://api.tvmaze.com/people/12842/castcredits
|
|
||||||
12872;http://api.tvmaze.com/people/12872/castcredits
|
|
||||||
12929;http://api.tvmaze.com/people/12929/castcredits
|
|
||||||
13169;http://api.tvmaze.com/people/13169/castcredits
|
|
||||||
13232;http://api.tvmaze.com/people/13232/castcredits
|
|
||||||
13262;http://api.tvmaze.com/people/13262/castcredits
|
|
||||||
13277;http://api.tvmaze.com/people/13277/castcredits
|
|
||||||
13509;http://api.tvmaze.com/people/13509/castcredits
|
|
||||||
13513;http://api.tvmaze.com/people/13513/castcredits
|
|
||||||
13516;http://api.tvmaze.com/people/13516/castcredits
|
|
||||||
13518;http://api.tvmaze.com/people/13518/castcredits
|
|
||||||
13519;http://api.tvmaze.com/people/13519/castcredits
|
|
||||||
13520;http://api.tvmaze.com/people/13520/castcredits
|
|
||||||
13679;http://api.tvmaze.com/people/13679/castcredits
|
|
||||||
13760;http://api.tvmaze.com/people/13760/castcredits
|
|
||||||
13774;http://api.tvmaze.com/people/13774/castcredits
|
|
||||||
13922;http://api.tvmaze.com/people/13922/castcredits
|
|
||||||
13957;http://api.tvmaze.com/people/13957/castcredits
|
|
||||||
13968;http://api.tvmaze.com/people/13968/castcredits
|
|
||||||
13997;http://api.tvmaze.com/people/13997/castcredits
|
|
||||||
14057;http://api.tvmaze.com/people/14057/castcredits
|
|
||||||
14099;http://api.tvmaze.com/people/14099/castcredits
|
|
||||||
14132;http://api.tvmaze.com/people/14132/castcredits
|
|
||||||
14159;http://api.tvmaze.com/people/14159/castcredits
|
|
||||||
14162;http://api.tvmaze.com/people/14162/castcredits
|
|
||||||
14307;http://api.tvmaze.com/people/14307/castcredits
|
|
||||||
14309;http://api.tvmaze.com/people/14309/castcredits
|
|
||||||
14310;http://api.tvmaze.com/people/14310/castcredits
|
|
||||||
14397;http://api.tvmaze.com/people/14397/castcredits
|
|
||||||
14436;http://api.tvmaze.com/people/14436/castcredits
|
|
||||||
14472;http://api.tvmaze.com/people/14472/castcredits
|
|
||||||
14501;http://api.tvmaze.com/people/14501/castcredits
|
|
||||||
14525;http://api.tvmaze.com/people/14525/castcredits
|
|
||||||
14541;http://api.tvmaze.com/people/14541/castcredits
|
|
||||||
14566;http://api.tvmaze.com/people/14566/castcredits
|
|
||||||
14572;http://api.tvmaze.com/people/14572/castcredits
|
|
||||||
14625;http://api.tvmaze.com/people/14625/castcredits
|
|
||||||
14627;http://api.tvmaze.com/people/14627/castcredits
|
|
||||||
14671;http://api.tvmaze.com/people/14671/castcredits
|
|
||||||
14672;http://api.tvmaze.com/people/14672/castcredits
|
|
||||||
14673;http://api.tvmaze.com/people/14673/castcredits
|
|
||||||
14674;http://api.tvmaze.com/people/14674/castcredits
|
|
||||||
14853;http://api.tvmaze.com/people/14853/castcredits
|
|
||||||
14864;http://api.tvmaze.com/people/14864/castcredits
|
|
||||||
14872;http://api.tvmaze.com/people/14872/castcredits
|
|
||||||
14960;http://api.tvmaze.com/people/14960/castcredits
|
|
||||||
14977;http://api.tvmaze.com/people/14977/castcredits
|
|
||||||
14996;http://api.tvmaze.com/people/14996/castcredits
|
|
||||||
15000;http://api.tvmaze.com/people/15000/castcredits
|
|
||||||
15021;http://api.tvmaze.com/people/15021/castcredits
|
|
||||||
15028;http://api.tvmaze.com/people/15028/castcredits
|
|
||||||
15029;http://api.tvmaze.com/people/15029/castcredits
|
|
||||||
15032;http://api.tvmaze.com/people/15032/castcredits
|
|
||||||
15036;http://api.tvmaze.com/people/15036/castcredits
|
|
||||||
15043;http://api.tvmaze.com/people/15043/castcredits
|
|
||||||
15048;http://api.tvmaze.com/people/15048/castcredits
|
|
||||||
15074;http://api.tvmaze.com/people/15074/castcredits
|
|
||||||
15098;http://api.tvmaze.com/people/15098/castcredits
|
|
||||||
15108;http://api.tvmaze.com/people/15108/castcredits
|
|
||||||
15114;http://api.tvmaze.com/people/15114/castcredits
|
|
||||||
15128;http://api.tvmaze.com/people/15128/castcredits
|
|
||||||
15147;http://api.tvmaze.com/people/15147/castcredits
|
|
||||||
15150;http://api.tvmaze.com/people/15150/castcredits
|
|
||||||
15172;http://api.tvmaze.com/people/15172/castcredits
|
|
||||||
15188;http://api.tvmaze.com/people/15188/castcredits
|
|
||||||
15189;http://api.tvmaze.com/people/15189/castcredits
|
|
||||||
15196;http://api.tvmaze.com/people/15196/castcredits
|
|
||||||
15219;http://api.tvmaze.com/people/15219/castcredits
|
|
||||||
15220;http://api.tvmaze.com/people/15220/castcredits
|
|
||||||
15234;http://api.tvmaze.com/people/15234/castcredits
|
|
||||||
15257;http://api.tvmaze.com/people/15257/castcredits
|
|
||||||
15270;http://api.tvmaze.com/people/15270/castcredits
|
|
||||||
15276;http://api.tvmaze.com/people/15276/castcredits
|
|
||||||
15279;http://api.tvmaze.com/people/15279/castcredits
|
|
||||||
15281;http://api.tvmaze.com/people/15281/castcredits
|
|
||||||
15285;http://api.tvmaze.com/people/15285/castcredits
|
|
||||||
15292;http://api.tvmaze.com/people/15292/castcredits
|
|
||||||
15295;http://api.tvmaze.com/people/15295/castcredits
|
|
||||||
15315;http://api.tvmaze.com/people/15315/castcredits
|
|
||||||
15344;http://api.tvmaze.com/people/15344/castcredits
|
|
||||||
15369;http://api.tvmaze.com/people/15369/castcredits
|
|
||||||
15371;http://api.tvmaze.com/people/15371/castcredits
|
|
||||||
15426;http://api.tvmaze.com/people/15426/castcredits
|
|
||||||
15443;http://api.tvmaze.com/people/15443/castcredits
|
|
||||||
15448;http://api.tvmaze.com/people/15448/castcredits
|
|
||||||
15449;http://api.tvmaze.com/people/15449/castcredits
|
|
||||||
15450;http://api.tvmaze.com/people/15450/castcredits
|
|
||||||
15451;http://api.tvmaze.com/people/15451/castcredits
|
|
||||||
15454;http://api.tvmaze.com/people/15454/castcredits
|
|
||||||
15473;http://api.tvmaze.com/people/15473/castcredits
|
|
||||||
15474;http://api.tvmaze.com/people/15474/castcredits
|
|
||||||
15496;http://api.tvmaze.com/people/15496/castcredits
|
|
||||||
15509;http://api.tvmaze.com/people/15509/castcredits
|
|
||||||
15520;http://api.tvmaze.com/people/15520/castcredits
|
|
||||||
15534;http://api.tvmaze.com/people/15534/castcredits
|
|
||||||
15535;http://api.tvmaze.com/people/15535/castcredits
|
|
||||||
15574;http://api.tvmaze.com/people/15574/castcredits
|
|
||||||
15587;http://api.tvmaze.com/people/15587/castcredits
|
|
||||||
15613;http://api.tvmaze.com/people/15613/castcredits
|
|
||||||
15630;http://api.tvmaze.com/people/15630/castcredits
|
|
||||||
15631;http://api.tvmaze.com/people/15631/castcredits
|
|
||||||
15643;http://api.tvmaze.com/people/15643/castcredits
|
|
||||||
15644;http://api.tvmaze.com/people/15644/castcredits
|
|
||||||
15645;http://api.tvmaze.com/people/15645/castcredits
|
|
||||||
15647;http://api.tvmaze.com/people/15647/castcredits
|
|
||||||
15654;http://api.tvmaze.com/people/15654/castcredits
|
|
||||||
15676;http://api.tvmaze.com/people/15676/castcredits
|
|
||||||
15677;http://api.tvmaze.com/people/15677/castcredits
|
|
||||||
15678;http://api.tvmaze.com/people/15678/castcredits
|
|
||||||
15688;http://api.tvmaze.com/people/15688/castcredits
|
|
||||||
15689;http://api.tvmaze.com/people/15689/castcredits
|
|
||||||
15693;http://api.tvmaze.com/people/15693/castcredits
|
|
||||||
15698;http://api.tvmaze.com/people/15698/castcredits
|
|
||||||
15701;http://api.tvmaze.com/people/15701/castcredits
|
|
||||||
15706;http://api.tvmaze.com/people/15706/castcredits
|
|
||||||
15716;http://api.tvmaze.com/people/15716/castcredits
|
|
||||||
15731;http://api.tvmaze.com/people/15731/castcredits
|
|
||||||
15751;http://api.tvmaze.com/people/15751/castcredits
|
|
||||||
15767;http://api.tvmaze.com/people/15767/castcredits
|
|
||||||
15771;http://api.tvmaze.com/people/15771/castcredits
|
|
||||||
15778;http://api.tvmaze.com/people/15778/castcredits
|
|
||||||
15812;http://api.tvmaze.com/people/15812/castcredits
|
|
||||||
15824;http://api.tvmaze.com/people/15824/castcredits
|
|
||||||
15826;http://api.tvmaze.com/people/15826/castcredits
|
|
||||||
15847;http://api.tvmaze.com/people/15847/castcredits
|
|
||||||
15855;http://api.tvmaze.com/people/15855/castcredits
|
|
||||||
15866;http://api.tvmaze.com/people/15866/castcredits
|
|
||||||
15882;http://api.tvmaze.com/people/15882/castcredits
|
|
||||||
15909;http://api.tvmaze.com/people/15909/castcredits
|
|
||||||
15928;http://api.tvmaze.com/people/15928/castcredits
|
|
||||||
16316;http://api.tvmaze.com/people/16316/castcredits
|
|
||||||
16317;http://api.tvmaze.com/people/16317/castcredits
|
|
||||||
16331;http://api.tvmaze.com/people/16331/castcredits
|
|
||||||
16332;http://api.tvmaze.com/people/16332/castcredits
|
|
||||||
16340;http://api.tvmaze.com/people/16340/castcredits
|
|
||||||
16341;http://api.tvmaze.com/people/16341/castcredits
|
|
||||||
16343;http://api.tvmaze.com/people/16343/castcredits
|
|
||||||
16348;http://api.tvmaze.com/people/16348/castcredits
|
|
||||||
16352;http://api.tvmaze.com/people/16352/castcredits
|
|
||||||
16362;http://api.tvmaze.com/people/16362/castcredits
|
|
||||||
16363;http://api.tvmaze.com/people/16363/castcredits
|
|
||||||
16367;http://api.tvmaze.com/people/16367/castcredits
|
|
||||||
16368;http://api.tvmaze.com/people/16368/castcredits
|
|
||||||
16383;http://api.tvmaze.com/people/16383/castcredits
|
|
||||||
16386;http://api.tvmaze.com/people/16386/castcredits
|
|
||||||
16387;http://api.tvmaze.com/people/16387/castcredits
|
|
||||||
16394;http://api.tvmaze.com/people/16394/castcredits
|
|
||||||
16395;http://api.tvmaze.com/people/16395/castcredits
|
|
||||||
16398;http://api.tvmaze.com/people/16398/castcredits
|
|
||||||
16399;http://api.tvmaze.com/people/16399/castcredits
|
|
||||||
16400;http://api.tvmaze.com/people/16400/castcredits
|
|
||||||
16404;http://api.tvmaze.com/people/16404/castcredits
|
|
||||||
16407;http://api.tvmaze.com/people/16407/castcredits
|
|
||||||
16417;http://api.tvmaze.com/people/16417/castcredits
|
|
||||||
16424;http://api.tvmaze.com/people/16424/castcredits
|
|
||||||
16435;http://api.tvmaze.com/people/16435/castcredits
|
|
||||||
16440;http://api.tvmaze.com/people/16440/castcredits
|
|
||||||
16442;http://api.tvmaze.com/people/16442/castcredits
|
|
||||||
16443;http://api.tvmaze.com/people/16443/castcredits
|
|
||||||
16448;http://api.tvmaze.com/people/16448/castcredits
|
|
||||||
16449;http://api.tvmaze.com/people/16449/castcredits
|
|
||||||
16469;http://api.tvmaze.com/people/16469/castcredits
|
|
||||||
16470;http://api.tvmaze.com/people/16470/castcredits
|
|
||||||
16473;http://api.tvmaze.com/people/16473/castcredits
|
|
||||||
16474;http://api.tvmaze.com/people/16474/castcredits
|
|
||||||
16475;http://api.tvmaze.com/people/16475/castcredits
|
|
||||||
16476;http://api.tvmaze.com/people/16476/castcredits
|
|
||||||
16481;http://api.tvmaze.com/people/16481/castcredits
|
|
||||||
16492;http://api.tvmaze.com/people/16492/castcredits
|
|
||||||
16493;http://api.tvmaze.com/people/16493/castcredits
|
|
||||||
16496;http://api.tvmaze.com/people/16496/castcredits
|
|
||||||
16497;http://api.tvmaze.com/people/16497/castcredits
|
|
||||||
16506;http://api.tvmaze.com/people/16506/castcredits
|
|
||||||
16507;http://api.tvmaze.com/people/16507/castcredits
|
|
||||||
16510;http://api.tvmaze.com/people/16510/castcredits
|
|
||||||
16511;http://api.tvmaze.com/people/16511/castcredits
|
|
||||||
16512;http://api.tvmaze.com/people/16512/castcredits
|
|
||||||
16514;http://api.tvmaze.com/people/16514/castcredits
|
|
||||||
16515;http://api.tvmaze.com/people/16515/castcredits
|
|
||||||
16522;http://api.tvmaze.com/people/16522/castcredits
|
|
||||||
16523;http://api.tvmaze.com/people/16523/castcredits
|
|
||||||
16528;http://api.tvmaze.com/people/16528/castcredits
|
|
||||||
16534;http://api.tvmaze.com/people/16534/castcredits
|
|
||||||
16535;http://api.tvmaze.com/people/16535/castcredits
|
|
||||||
16539;http://api.tvmaze.com/people/16539/castcredits
|
|
||||||
16540;http://api.tvmaze.com/people/16540/castcredits
|
|
||||||
16542;http://api.tvmaze.com/people/16542/castcredits
|
|
||||||
16547;http://api.tvmaze.com/people/16547/castcredits
|
|
||||||
16548;http://api.tvmaze.com/people/16548/castcredits
|
|
||||||
16549;http://api.tvmaze.com/people/16549/castcredits
|
|
||||||
16551;http://api.tvmaze.com/people/16551/castcredits
|
|
||||||
16552;http://api.tvmaze.com/people/16552/castcredits
|
|
||||||
16559;http://api.tvmaze.com/people/16559/castcredits
|
|
||||||
16563;http://api.tvmaze.com/people/16563/castcredits
|
|
||||||
16564;http://api.tvmaze.com/people/16564/castcredits
|
|
||||||
16567;http://api.tvmaze.com/people/16567/castcredits
|
|
||||||
16568;http://api.tvmaze.com/people/16568/castcredits
|
|
||||||
16569;http://api.tvmaze.com/people/16569/castcredits
|
|
||||||
16574;http://api.tvmaze.com/people/16574/castcredits
|
|
||||||
16578;http://api.tvmaze.com/people/16578/castcredits
|
|
||||||
16579;http://api.tvmaze.com/people/16579/castcredits
|
|
||||||
16580;http://api.tvmaze.com/people/16580/castcredits
|
|
||||||
16586;http://api.tvmaze.com/people/16586/castcredits
|
|
||||||
16587;http://api.tvmaze.com/people/16587/castcredits
|
|
||||||
16588;http://api.tvmaze.com/people/16588/castcredits
|
|
||||||
16593;http://api.tvmaze.com/people/16593/castcredits
|
|
||||||
16596;http://api.tvmaze.com/people/16596/castcredits
|
|
||||||
16597;http://api.tvmaze.com/people/16597/castcredits
|
|
||||||
16598;http://api.tvmaze.com/people/16598/castcredits
|
|
||||||
16601;http://api.tvmaze.com/people/16601/castcredits
|
|
||||||
16602;http://api.tvmaze.com/people/16602/castcredits
|
|
||||||
16605;http://api.tvmaze.com/people/16605/castcredits
|
|
||||||
16617;http://api.tvmaze.com/people/16617/castcredits
|
|
||||||
16618;http://api.tvmaze.com/people/16618/castcredits
|
|
||||||
16626;http://api.tvmaze.com/people/16626/castcredits
|
|
||||||
16627;http://api.tvmaze.com/people/16627/castcredits
|
|
||||||
16628;http://api.tvmaze.com/people/16628/castcredits
|
|
||||||
16633;http://api.tvmaze.com/people/16633/castcredits
|
|
||||||
16634;http://api.tvmaze.com/people/16634/castcredits
|
|
||||||
16635;http://api.tvmaze.com/people/16635/castcredits
|
|
||||||
16642;http://api.tvmaze.com/people/16642/castcredits
|
|
||||||
16643;http://api.tvmaze.com/people/16643/castcredits
|
|
||||||
16649;http://api.tvmaze.com/people/16649/castcredits
|
|
||||||
16651;http://api.tvmaze.com/people/16651/castcredits
|
|
||||||
16657;http://api.tvmaze.com/people/16657/castcredits
|
|
||||||
16664;http://api.tvmaze.com/people/16664/castcredits
|
|
||||||
16665;http://api.tvmaze.com/people/16665/castcredits
|
|
||||||
16666;http://api.tvmaze.com/people/16666/castcredits
|
|
||||||
16670;http://api.tvmaze.com/people/16670/castcredits
|
|
||||||
16671;http://api.tvmaze.com/people/16671/castcredits
|
|
||||||
16672;http://api.tvmaze.com/people/16672/castcredits
|
|
||||||
16678;http://api.tvmaze.com/people/16678/castcredits
|
|
||||||
16680;http://api.tvmaze.com/people/16680/castcredits
|
|
||||||
16685;http://api.tvmaze.com/people/16685/castcredits
|
|
||||||
16690;http://api.tvmaze.com/people/16690/castcredits
|
|
||||||
16691;http://api.tvmaze.com/people/16691/castcredits
|
|
||||||
16692;http://api.tvmaze.com/people/16692/castcredits
|
|
||||||
16696;http://api.tvmaze.com/people/16696/castcredits
|
|
||||||
16697;http://api.tvmaze.com/people/16697/castcredits
|
|
||||||
16698;http://api.tvmaze.com/people/16698/castcredits
|
|
||||||
16704;http://api.tvmaze.com/people/16704/castcredits
|
|
||||||
16705;http://api.tvmaze.com/people/16705/castcredits
|
|
||||||
16706;http://api.tvmaze.com/people/16706/castcredits
|
|
||||||
16708;http://api.tvmaze.com/people/16708/castcredits
|
|
||||||
16712;http://api.tvmaze.com/people/16712/castcredits
|
|
||||||
16718;http://api.tvmaze.com/people/16718/castcredits
|
|
||||||
16719;http://api.tvmaze.com/people/16719/castcredits
|
|
||||||
16720;http://api.tvmaze.com/people/16720/castcredits
|
|
||||||
16726;http://api.tvmaze.com/people/16726/castcredits
|
|
||||||
16727;http://api.tvmaze.com/people/16727/castcredits
|
|
||||||
16737;http://api.tvmaze.com/people/16737/castcredits
|
|
||||||
16738;http://api.tvmaze.com/people/16738/castcredits
|
|
||||||
16741;http://api.tvmaze.com/people/16741/castcredits
|
|
||||||
16751;http://api.tvmaze.com/people/16751/castcredits
|
|
||||||
16753;http://api.tvmaze.com/people/16753/castcredits
|
|
||||||
16759;http://api.tvmaze.com/people/16759/castcredits
|
|
||||||
16766;http://api.tvmaze.com/people/16766/castcredits
|
|
||||||
16769;http://api.tvmaze.com/people/16769/castcredits
|
|
||||||
16771;http://api.tvmaze.com/people/16771/castcredits
|
|
||||||
16787;http://api.tvmaze.com/people/16787/castcredits
|
|
||||||
16788;http://api.tvmaze.com/people/16788/castcredits
|
|
||||||
16797;http://api.tvmaze.com/people/16797/castcredits
|
|
||||||
16798;http://api.tvmaze.com/people/16798/castcredits
|
|
||||||
16802;http://api.tvmaze.com/people/16802/castcredits
|
|
||||||
16803;http://api.tvmaze.com/people/16803/castcredits
|
|
||||||
16804;http://api.tvmaze.com/people/16804/castcredits
|
|
||||||
16808;http://api.tvmaze.com/people/16808/castcredits
|
|
||||||
16813;http://api.tvmaze.com/people/16813/castcredits
|
|
||||||
16814;http://api.tvmaze.com/people/16814/castcredits
|
|
||||||
16817;http://api.tvmaze.com/people/16817/castcredits
|
|
||||||
16824;http://api.tvmaze.com/people/16824/castcredits
|
|
||||||
16825;http://api.tvmaze.com/people/16825/castcredits
|
|
||||||
16826;http://api.tvmaze.com/people/16826/castcredits
|
|
||||||
16827;http://api.tvmaze.com/people/16827/castcredits
|
|
||||||
16828;http://api.tvmaze.com/people/16828/castcredits
|
|
||||||
16834;http://api.tvmaze.com/people/16834/castcredits
|
|
||||||
16835;http://api.tvmaze.com/people/16835/castcredits
|
|
||||||
16836;http://api.tvmaze.com/people/16836/castcredits
|
|
||||||
16837;http://api.tvmaze.com/people/16837/castcredits
|
|
||||||
16838;http://api.tvmaze.com/people/16838/castcredits
|
|
||||||
16839;http://api.tvmaze.com/people/16839/castcredits
|
|
||||||
16844;http://api.tvmaze.com/people/16844/castcredits
|
|
||||||
16845;http://api.tvmaze.com/people/16845/castcredits
|
|
||||||
16854;http://api.tvmaze.com/people/16854/castcredits
|
|
||||||
16858;http://api.tvmaze.com/people/16858/castcredits
|
|
||||||
16859;http://api.tvmaze.com/people/16859/castcredits
|
|
||||||
16971;http://api.tvmaze.com/people/16971/castcredits
|
|
||||||
17044;http://api.tvmaze.com/people/17044/castcredits
|
|
||||||
17049;http://api.tvmaze.com/people/17049/castcredits
|
|
||||||
17306;http://api.tvmaze.com/people/17306/castcredits
|
|
||||||
17334;http://api.tvmaze.com/people/17334/castcredits
|
|
||||||
17371;http://api.tvmaze.com/people/17371/castcredits
|
|
||||||
17445;http://api.tvmaze.com/people/17445/castcredits
|
|
||||||
17470;http://api.tvmaze.com/people/17470/castcredits
|
|
||||||
17505;http://api.tvmaze.com/people/17505/castcredits
|
|
||||||
17558;http://api.tvmaze.com/people/17558/castcredits
|
|
||||||
17588;http://api.tvmaze.com/people/17588/castcredits
|
|
||||||
17640;http://api.tvmaze.com/people/17640/castcredits
|
|
||||||
17657;http://api.tvmaze.com/people/17657/castcredits
|
|
||||||
17719;http://api.tvmaze.com/people/17719/castcredits
|
|
||||||
17736;http://api.tvmaze.com/people/17736/castcredits
|
|
||||||
17783;http://api.tvmaze.com/people/17783/castcredits
|
|
||||||
17818;http://api.tvmaze.com/people/17818/castcredits
|
|
||||||
17890;http://api.tvmaze.com/people/17890/castcredits
|
|
||||||
17898;http://api.tvmaze.com/people/17898/castcredits
|
|
||||||
17954;http://api.tvmaze.com/people/17954/castcredits
|
|
||||||
18043;http://api.tvmaze.com/people/18043/castcredits
|
|
||||||
18080;http://api.tvmaze.com/people/18080/castcredits
|
|
||||||
18086;http://api.tvmaze.com/people/18086/castcredits
|
|
||||||
18308;http://api.tvmaze.com/people/18308/castcredits
|
|
||||||
18469;http://api.tvmaze.com/people/18469/castcredits
|
|
||||||
18508;http://api.tvmaze.com/people/18508/castcredits
|
|
||||||
18676;http://api.tvmaze.com/people/18676/castcredits
|
|
||||||
18880;http://api.tvmaze.com/people/18880/castcredits
|
|
||||||
18882;http://api.tvmaze.com/people/18882/castcredits
|
|
||||||
19047;http://api.tvmaze.com/people/19047/castcredits
|
|
||||||
19107;http://api.tvmaze.com/people/19107/castcredits
|
|
||||||
19194;http://api.tvmaze.com/people/19194/castcredits
|
|
||||||
19205;http://api.tvmaze.com/people/19205/castcredits
|
|
||||||
19214;http://api.tvmaze.com/people/19214/castcredits
|
|
||||||
19228;http://api.tvmaze.com/people/19228/castcredits
|
|
||||||
19441;http://api.tvmaze.com/people/19441/castcredits
|
|
||||||
19502;http://api.tvmaze.com/people/19502/castcredits
|
|
||||||
19504;http://api.tvmaze.com/people/19504/castcredits
|
|
||||||
19536;http://api.tvmaze.com/people/19536/castcredits
|
|
||||||
19559;http://api.tvmaze.com/people/19559/castcredits
|
|
||||||
19606;http://api.tvmaze.com/people/19606/castcredits
|
|
||||||
19611;http://api.tvmaze.com/people/19611/castcredits
|
|
||||||
19619;http://api.tvmaze.com/people/19619/castcredits
|
|
||||||
19640;http://api.tvmaze.com/people/19640/castcredits
|
|
||||||
19686;http://api.tvmaze.com/people/19686/castcredits
|
|
||||||
19689;http://api.tvmaze.com/people/19689/castcredits
|
|
||||||
19690;http://api.tvmaze.com/people/19690/castcredits
|
|
||||||
19691;http://api.tvmaze.com/people/19691/castcredits
|
|
||||||
19699;http://api.tvmaze.com/people/19699/castcredits
|
|
||||||
19769;http://api.tvmaze.com/people/19769/castcredits
|
|
||||||
19788;http://api.tvmaze.com/people/19788/castcredits
|
|
||||||
19840;http://api.tvmaze.com/people/19840/castcredits
|
|
||||||
20084;http://api.tvmaze.com/people/20084/castcredits
|
|
||||||
20213;http://api.tvmaze.com/people/20213/castcredits
|
|
||||||
20287;http://api.tvmaze.com/people/20287/castcredits
|
|
||||||
20289;http://api.tvmaze.com/people/20289/castcredits
|
|
||||||
20327;http://api.tvmaze.com/people/20327/castcredits
|
|
||||||
20385;http://api.tvmaze.com/people/20385/castcredits
|
|
||||||
20402;http://api.tvmaze.com/people/20402/castcredits
|
|
||||||
20421;http://api.tvmaze.com/people/20421/castcredits
|
|
||||||
20463;http://api.tvmaze.com/people/20463/castcredits
|
|
||||||
20468;http://api.tvmaze.com/people/20468/castcredits
|
|
||||||
20540;http://api.tvmaze.com/people/20540/castcredits
|
|
||||||
20570;http://api.tvmaze.com/people/20570/castcredits
|
|
||||||
20580;http://api.tvmaze.com/people/20580/castcredits
|
|
||||||
20647;http://api.tvmaze.com/people/20647/castcredits
|
|
||||||
20668;http://api.tvmaze.com/people/20668/castcredits
|
|
||||||
20671;http://api.tvmaze.com/people/20671/castcredits
|
|
||||||
20797;http://api.tvmaze.com/people/20797/castcredits
|
|
||||||
20811;http://api.tvmaze.com/people/20811/castcredits
|
|
||||||
20812;http://api.tvmaze.com/people/20812/castcredits
|
|
||||||
20813;http://api.tvmaze.com/people/20813/castcredits
|
|
||||||
20814;http://api.tvmaze.com/people/20814/castcredits
|
|
||||||
20822;http://api.tvmaze.com/people/20822/castcredits
|
|
||||||
20830;http://api.tvmaze.com/people/20830/castcredits
|
|
||||||
20862;http://api.tvmaze.com/people/20862/castcredits
|
|
||||||
20886;http://api.tvmaze.com/people/20886/castcredits
|
|
||||||
20896;http://api.tvmaze.com/people/20896/castcredits
|
|
||||||
20905;http://api.tvmaze.com/people/20905/castcredits
|
|
||||||
20907;http://api.tvmaze.com/people/20907/castcredits
|
|
||||||
20929;http://api.tvmaze.com/people/20929/castcredits
|
|
||||||
21001;http://api.tvmaze.com/people/21001/castcredits
|
|
||||||
21003;http://api.tvmaze.com/people/21003/castcredits
|
|
||||||
21035;http://api.tvmaze.com/people/21035/castcredits
|
|
||||||
21057;http://api.tvmaze.com/people/21057/castcredits
|
|
||||||
21062;http://api.tvmaze.com/people/21062/castcredits
|
|
||||||
21075;http://api.tvmaze.com/people/21075/castcredits
|
|
||||||
21077;http://api.tvmaze.com/people/21077/castcredits
|
|
||||||
21078;http://api.tvmaze.com/people/21078/castcredits
|
|
||||||
21084;http://api.tvmaze.com/people/21084/castcredits
|
|
||||||
21085;http://api.tvmaze.com/people/21085/castcredits
|
|
||||||
21128;http://api.tvmaze.com/people/21128/castcredits
|
|
||||||
21165;http://api.tvmaze.com/people/21165/castcredits
|
|
||||||
21170;http://api.tvmaze.com/people/21170/castcredits
|
|
||||||
21171;http://api.tvmaze.com/people/21171/castcredits
|
|
||||||
21176;http://api.tvmaze.com/people/21176/castcredits
|
|
||||||
21177;http://api.tvmaze.com/people/21177/castcredits
|
|
||||||
21181;http://api.tvmaze.com/people/21181/castcredits
|
|
||||||
21185;http://api.tvmaze.com/people/21185/castcredits
|
|
||||||
21186;http://api.tvmaze.com/people/21186/castcredits
|
|
||||||
21189;http://api.tvmaze.com/people/21189/castcredits
|
|
||||||
21190;http://api.tvmaze.com/people/21190/castcredits
|
|
||||||
21191;http://api.tvmaze.com/people/21191/castcredits
|
|
||||||
21192;http://api.tvmaze.com/people/21192/castcredits
|
|
||||||
21193;http://api.tvmaze.com/people/21193/castcredits
|
|
||||||
21196;http://api.tvmaze.com/people/21196/castcredits
|
|
||||||
21198;http://api.tvmaze.com/people/21198/castcredits
|
|
||||||
21199;http://api.tvmaze.com/people/21199/castcredits
|
|
||||||
21203;http://api.tvmaze.com/people/21203/castcredits
|
|
||||||
21204;http://api.tvmaze.com/people/21204/castcredits
|
|
||||||
21205;http://api.tvmaze.com/people/21205/castcredits
|
|
||||||
21206;http://api.tvmaze.com/people/21206/castcredits
|
|
||||||
21208;http://api.tvmaze.com/people/21208/castcredits
|
|
||||||
21209;http://api.tvmaze.com/people/21209/castcredits
|
|
||||||
21210;http://api.tvmaze.com/people/21210/castcredits
|
|
||||||
21212;http://api.tvmaze.com/people/21212/castcredits
|
|
||||||
21213;http://api.tvmaze.com/people/21213/castcredits
|
|
||||||
21214;http://api.tvmaze.com/people/21214/castcredits
|
|
||||||
21218;http://api.tvmaze.com/people/21218/castcredits
|
|
||||||
21219;http://api.tvmaze.com/people/21219/castcredits
|
|
||||||
21220;http://api.tvmaze.com/people/21220/castcredits
|
|
||||||
21222;http://api.tvmaze.com/people/21222/castcredits
|
|
||||||
21223;http://api.tvmaze.com/people/21223/castcredits
|
|
||||||
21224;http://api.tvmaze.com/people/21224/castcredits
|
|
||||||
21225;http://api.tvmaze.com/people/21225/castcredits
|
|
||||||
21226;http://api.tvmaze.com/people/21226/castcredits
|
|
||||||
21227;http://api.tvmaze.com/people/21227/castcredits
|
|
||||||
21228;http://api.tvmaze.com/people/21228/castcredits
|
|
||||||
21232;http://api.tvmaze.com/people/21232/castcredits
|
|
||||||
21233;http://api.tvmaze.com/people/21233/castcredits
|
|
||||||
21234;http://api.tvmaze.com/people/21234/castcredits
|
|
||||||
21235;http://api.tvmaze.com/people/21235/castcredits
|
|
||||||
21236;http://api.tvmaze.com/people/21236/castcredits
|
|
||||||
21237;http://api.tvmaze.com/people/21237/castcredits
|
|
||||||
21238;http://api.tvmaze.com/people/21238/castcredits
|
|
||||||
21239;http://api.tvmaze.com/people/21239/castcredits
|
|
||||||
21242;http://api.tvmaze.com/people/21242/castcredits
|
|
||||||
21243;http://api.tvmaze.com/people/21243/castcredits
|
|
||||||
21244;http://api.tvmaze.com/people/21244/castcredits
|
|
||||||
21245;http://api.tvmaze.com/people/21245/castcredits
|
|
||||||
21246;http://api.tvmaze.com/people/21246/castcredits
|
|
||||||
21249;http://api.tvmaze.com/people/21249/castcredits
|
|
||||||
21250;http://api.tvmaze.com/people/21250/castcredits
|
|
||||||
21251;http://api.tvmaze.com/people/21251/castcredits
|
|
||||||
21253;http://api.tvmaze.com/people/21253/castcredits
|
|
||||||
21254;http://api.tvmaze.com/people/21254/castcredits
|
|
||||||
21255;http://api.tvmaze.com/people/21255/castcredits
|
|
||||||
21256;http://api.tvmaze.com/people/21256/castcredits
|
|
||||||
21262;http://api.tvmaze.com/people/21262/castcredits
|
|
||||||
21263;http://api.tvmaze.com/people/21263/castcredits
|
|
||||||
21264;http://api.tvmaze.com/people/21264/castcredits
|
|
||||||
21265;http://api.tvmaze.com/people/21265/castcredits
|
|
||||||
21266;http://api.tvmaze.com/people/21266/castcredits
|
|
||||||
21267;http://api.tvmaze.com/people/21267/castcredits
|
|
||||||
21270;http://api.tvmaze.com/people/21270/castcredits
|
|
||||||
21272;http://api.tvmaze.com/people/21272/castcredits
|
|
||||||
21273;http://api.tvmaze.com/people/21273/castcredits
|
|
||||||
21275;http://api.tvmaze.com/people/21275/castcredits
|
|
||||||
21276;http://api.tvmaze.com/people/21276/castcredits
|
|
||||||
21277;http://api.tvmaze.com/people/21277/castcredits
|
|
||||||
21278;http://api.tvmaze.com/people/21278/castcredits
|
|
||||||
21279;http://api.tvmaze.com/people/21279/castcredits
|
|
||||||
21280;http://api.tvmaze.com/people/21280/castcredits
|
|
||||||
21281;http://api.tvmaze.com/people/21281/castcredits
|
|
||||||
21282;http://api.tvmaze.com/people/21282/castcredits
|
|
||||||
21283;http://api.tvmaze.com/people/21283/castcredits
|
|
||||||
21284;http://api.tvmaze.com/people/21284/castcredits
|
|
||||||
21285;http://api.tvmaze.com/people/21285/castcredits
|
|
||||||
21287;http://api.tvmaze.com/people/21287/castcredits
|
|
||||||
21288;http://api.tvmaze.com/people/21288/castcredits
|
|
||||||
21289;http://api.tvmaze.com/people/21289/castcredits
|
|
||||||
21290;http://api.tvmaze.com/people/21290/castcredits
|
|
||||||
21291;http://api.tvmaze.com/people/21291/castcredits
|
|
||||||
21292;http://api.tvmaze.com/people/21292/castcredits
|
|
||||||
21293;http://api.tvmaze.com/people/21293/castcredits
|
|
||||||
21294;http://api.tvmaze.com/people/21294/castcredits
|
|
||||||
21295;http://api.tvmaze.com/people/21295/castcredits
|
|
||||||
21296;http://api.tvmaze.com/people/21296/castcredits
|
|
||||||
21297;http://api.tvmaze.com/people/21297/castcredits
|
|
||||||
21298;http://api.tvmaze.com/people/21298/castcredits
|
|
||||||
21299;http://api.tvmaze.com/people/21299/castcredits
|
|
||||||
21300;http://api.tvmaze.com/people/21300/castcredits
|
|
||||||
21301;http://api.tvmaze.com/people/21301/castcredits
|
|
||||||
21303;http://api.tvmaze.com/people/21303/castcredits
|
|
||||||
21304;http://api.tvmaze.com/people/21304/castcredits
|
|
||||||
21305;http://api.tvmaze.com/people/21305/castcredits
|
|
||||||
21307;http://api.tvmaze.com/people/21307/castcredits
|
|
||||||
21308;http://api.tvmaze.com/people/21308/castcredits
|
|
||||||
21313;http://api.tvmaze.com/people/21313/castcredits
|
|
||||||
21314;http://api.tvmaze.com/people/21314/castcredits
|
|
||||||
21317;http://api.tvmaze.com/people/21317/castcredits
|
|
||||||
21319;http://api.tvmaze.com/people/21319/castcredits
|
|
||||||
21320;http://api.tvmaze.com/people/21320/castcredits
|
|
||||||
21321;http://api.tvmaze.com/people/21321/castcredits
|
|
||||||
21322;http://api.tvmaze.com/people/21322/castcredits
|
|
||||||
21323;http://api.tvmaze.com/people/21323/castcredits
|
|
||||||
21327;http://api.tvmaze.com/people/21327/castcredits
|
|
||||||
21328;http://api.tvmaze.com/people/21328/castcredits
|
|
||||||
21329;http://api.tvmaze.com/people/21329/castcredits
|
|
||||||
21330;http://api.tvmaze.com/people/21330/castcredits
|
|
||||||
21331;http://api.tvmaze.com/people/21331/castcredits
|
|
||||||
21332;http://api.tvmaze.com/people/21332/castcredits
|
|
||||||
21335;http://api.tvmaze.com/people/21335/castcredits
|
|
||||||
21338;http://api.tvmaze.com/people/21338/castcredits
|
|
||||||
21339;http://api.tvmaze.com/people/21339/castcredits
|
|
||||||
21342;http://api.tvmaze.com/people/21342/castcredits
|
|
||||||
21343;http://api.tvmaze.com/people/21343/castcredits
|
|
||||||
21344;http://api.tvmaze.com/people/21344/castcredits
|
|
||||||
21345;http://api.tvmaze.com/people/21345/castcredits
|
|
||||||
21410;http://api.tvmaze.com/people/21410/castcredits
|
|
||||||
21416;http://api.tvmaze.com/people/21416/castcredits
|
|
||||||
21420;http://api.tvmaze.com/people/21420/castcredits
|
|
||||||
21435;http://api.tvmaze.com/people/21435/castcredits
|
|
||||||
21438;http://api.tvmaze.com/people/21438/castcredits
|
|
||||||
21447;http://api.tvmaze.com/people/21447/castcredits
|
|
||||||
21449;http://api.tvmaze.com/people/21449/castcredits
|
|
||||||
21458;http://api.tvmaze.com/people/21458/castcredits
|
|
||||||
21459;http://api.tvmaze.com/people/21459/castcredits
|
|
||||||
21465;http://api.tvmaze.com/people/21465/castcredits
|
|
||||||
21466;http://api.tvmaze.com/people/21466/castcredits
|
|
||||||
21467;http://api.tvmaze.com/people/21467/castcredits
|
|
||||||
21468;http://api.tvmaze.com/people/21468/castcredits
|
|
||||||
21481;http://api.tvmaze.com/people/21481/castcredits
|
|
||||||
21488;http://api.tvmaze.com/people/21488/castcredits
|
|
||||||
21501;http://api.tvmaze.com/people/21501/castcredits
|
|
||||||
21502;http://api.tvmaze.com/people/21502/castcredits
|
|
||||||
21509;http://api.tvmaze.com/people/21509/castcredits
|
|
||||||
21510;http://api.tvmaze.com/people/21510/castcredits
|
|
||||||
21519;http://api.tvmaze.com/people/21519/castcredits
|
|
||||||
21520;http://api.tvmaze.com/people/21520/castcredits
|
|
||||||
21521;http://api.tvmaze.com/people/21521/castcredits
|
|
||||||
21522;http://api.tvmaze.com/people/21522/castcredits
|
|
||||||
21523;http://api.tvmaze.com/people/21523/castcredits
|
|
||||||
21534;http://api.tvmaze.com/people/21534/castcredits
|
|
||||||
21535;http://api.tvmaze.com/people/21535/castcredits
|
|
||||||
21539;http://api.tvmaze.com/people/21539/castcredits
|
|
||||||
21545;http://api.tvmaze.com/people/21545/castcredits
|
|
||||||
21549;http://api.tvmaze.com/people/21549/castcredits
|
|
||||||
21572;http://api.tvmaze.com/people/21572/castcredits
|
|
||||||
21581;http://api.tvmaze.com/people/21581/castcredits
|
|
||||||
21584;http://api.tvmaze.com/people/21584/castcredits
|
|
||||||
21585;http://api.tvmaze.com/people/21585/castcredits
|
|
||||||
21592;http://api.tvmaze.com/people/21592/castcredits
|
|
||||||
21599;http://api.tvmaze.com/people/21599/castcredits
|
|
||||||
21609;http://api.tvmaze.com/people/21609/castcredits
|
|
||||||
21614;http://api.tvmaze.com/people/21614/castcredits
|
|
||||||
21622;http://api.tvmaze.com/people/21622/castcredits
|
|
||||||
21623;http://api.tvmaze.com/people/21623/castcredits
|
|
||||||
21627;http://api.tvmaze.com/people/21627/castcredits
|
|
||||||
21628;http://api.tvmaze.com/people/21628/castcredits
|
|
||||||
21642;http://api.tvmaze.com/people/21642/castcredits
|
|
||||||
21643;http://api.tvmaze.com/people/21643/castcredits
|
|
||||||
21644;http://api.tvmaze.com/people/21644/castcredits
|
|
||||||
21645;http://api.tvmaze.com/people/21645/castcredits
|
|
||||||
21648;http://api.tvmaze.com/people/21648/castcredits
|
|
||||||
21649;http://api.tvmaze.com/people/21649/castcredits
|
|
||||||
21652;http://api.tvmaze.com/people/21652/castcredits
|
|
||||||
21653;http://api.tvmaze.com/people/21653/castcredits
|
|
||||||
21666;http://api.tvmaze.com/people/21666/castcredits
|
|
||||||
21667;http://api.tvmaze.com/people/21667/castcredits
|
|
||||||
21671;http://api.tvmaze.com/people/21671/castcredits
|
|
||||||
21674;http://api.tvmaze.com/people/21674/castcredits
|
|
||||||
21695;http://api.tvmaze.com/people/21695/castcredits
|
|
||||||
21709;http://api.tvmaze.com/people/21709/castcredits
|
|
||||||
21710;http://api.tvmaze.com/people/21710/castcredits
|
|
||||||
21719;http://api.tvmaze.com/people/21719/castcredits
|
|
||||||
21720;http://api.tvmaze.com/people/21720/castcredits
|
|
||||||
21721;http://api.tvmaze.com/people/21721/castcredits
|
|
||||||
21739;http://api.tvmaze.com/people/21739/castcredits
|
|
||||||
21744;http://api.tvmaze.com/people/21744/castcredits
|
|
||||||
21745;http://api.tvmaze.com/people/21745/castcredits
|
|
||||||
21752;http://api.tvmaze.com/people/21752/castcredits
|
|
||||||
21755;http://api.tvmaze.com/people/21755/castcredits
|
|
||||||
21756;http://api.tvmaze.com/people/21756/castcredits
|
|
||||||
21770;http://api.tvmaze.com/people/21770/castcredits
|
|
||||||
21776;http://api.tvmaze.com/people/21776/castcredits
|
|
||||||
21783;http://api.tvmaze.com/people/21783/castcredits
|
|
||||||
21789;http://api.tvmaze.com/people/21789/castcredits
|
|
||||||
21790;http://api.tvmaze.com/people/21790/castcredits
|
|
||||||
21799;http://api.tvmaze.com/people/21799/castcredits
|
|
||||||
21806;http://api.tvmaze.com/people/21806/castcredits
|
|
||||||
21807;http://api.tvmaze.com/people/21807/castcredits
|
|
||||||
21821;http://api.tvmaze.com/people/21821/castcredits
|
|
||||||
21822;http://api.tvmaze.com/people/21822/castcredits
|
|
||||||
21836;http://api.tvmaze.com/people/21836/castcredits
|
|
||||||
21840;http://api.tvmaze.com/people/21840/castcredits
|
|
||||||
21841;http://api.tvmaze.com/people/21841/castcredits
|
|
||||||
21842;http://api.tvmaze.com/people/21842/castcredits
|
|
||||||
21844;http://api.tvmaze.com/people/21844/castcredits
|
|
||||||
21845;http://api.tvmaze.com/people/21845/castcredits
|
|
||||||
21854;http://api.tvmaze.com/people/21854/castcredits
|
|
||||||
21855;http://api.tvmaze.com/people/21855/castcredits
|
|
||||||
21872;http://api.tvmaze.com/people/21872/castcredits
|
|
||||||
21873;http://api.tvmaze.com/people/21873/castcredits
|
|
||||||
21874;http://api.tvmaze.com/people/21874/castcredits
|
|
||||||
21883;http://api.tvmaze.com/people/21883/castcredits
|
|
||||||
21884;http://api.tvmaze.com/people/21884/castcredits
|
|
||||||
21885;http://api.tvmaze.com/people/21885/castcredits
|
|
||||||
21886;http://api.tvmaze.com/people/21886/castcredits
|
|
||||||
21901;http://api.tvmaze.com/people/21901/castcredits
|
|
||||||
21902;http://api.tvmaze.com/people/21902/castcredits
|
|
||||||
21905;http://api.tvmaze.com/people/21905/castcredits
|
|
||||||
21906;http://api.tvmaze.com/people/21906/castcredits
|
|
||||||
21911;http://api.tvmaze.com/people/21911/castcredits
|
|
||||||
21919;http://api.tvmaze.com/people/21919/castcredits
|
|
||||||
21920;http://api.tvmaze.com/people/21920/castcredits
|
|
||||||
21925;http://api.tvmaze.com/people/21925/castcredits
|
|
||||||
21928;http://api.tvmaze.com/people/21928/castcredits
|
|
||||||
21930;http://api.tvmaze.com/people/21930/castcredits
|
|
||||||
21936;http://api.tvmaze.com/people/21936/castcredits
|
|
||||||
21939;http://api.tvmaze.com/people/21939/castcredits
|
|
||||||
21942;http://api.tvmaze.com/people/21942/castcredits
|
|
||||||
21943;http://api.tvmaze.com/people/21943/castcredits
|
|
||||||
22007;http://api.tvmaze.com/people/22007/castcredits
|
|
||||||
22059;http://api.tvmaze.com/people/22059/castcredits
|
|
||||||
22063;http://api.tvmaze.com/people/22063/castcredits
|
|
||||||
22064;http://api.tvmaze.com/people/22064/castcredits
|
|
||||||
22069;http://api.tvmaze.com/people/22069/castcredits
|
|
||||||
22076;http://api.tvmaze.com/people/22076/castcredits
|
|
||||||
22078;http://api.tvmaze.com/people/22078/castcredits
|
|
||||||
22086;http://api.tvmaze.com/people/22086/castcredits
|
|
||||||
22095;http://api.tvmaze.com/people/22095/castcredits
|
|
||||||
22099;http://api.tvmaze.com/people/22099/castcredits
|
|
||||||
22104;http://api.tvmaze.com/people/22104/castcredits
|
|
||||||
22146;http://api.tvmaze.com/people/22146/castcredits
|
|
||||||
22147;http://api.tvmaze.com/people/22147/castcredits
|
|
||||||
22172;http://api.tvmaze.com/people/22172/castcredits
|
|
||||||
22176;http://api.tvmaze.com/people/22176/castcredits
|
|
||||||
22193;http://api.tvmaze.com/people/22193/castcredits
|
|
||||||
22198;http://api.tvmaze.com/people/22198/castcredits
|
|
||||||
22199;http://api.tvmaze.com/people/22199/castcredits
|
|
||||||
22204;http://api.tvmaze.com/people/22204/castcredits
|
|
||||||
22211;http://api.tvmaze.com/people/22211/castcredits
|
|
||||||
22214;http://api.tvmaze.com/people/22214/castcredits
|
|
||||||
22215;http://api.tvmaze.com/people/22215/castcredits
|
|
||||||
22222;http://api.tvmaze.com/people/22222/castcredits
|
|
||||||
22223;http://api.tvmaze.com/people/22223/castcredits
|
|
||||||
22243;http://api.tvmaze.com/people/22243/castcredits
|
|
||||||
22245;http://api.tvmaze.com/people/22245/castcredits
|
|
||||||
22250;http://api.tvmaze.com/people/22250/castcredits
|
|
||||||
22321;http://api.tvmaze.com/people/22321/castcredits
|
|
||||||
22395;http://api.tvmaze.com/people/22395/castcredits
|
|
||||||
22458;http://api.tvmaze.com/people/22458/castcredits
|
|
||||||
22522;http://api.tvmaze.com/people/22522/castcredits
|
|
||||||
22536;http://api.tvmaze.com/people/22536/castcredits
|
|
||||||
22697;http://api.tvmaze.com/people/22697/castcredits
|
|
||||||
22792;http://api.tvmaze.com/people/22792/castcredits
|
|
||||||
22798;http://api.tvmaze.com/people/22798/castcredits
|
|
||||||
22803;http://api.tvmaze.com/people/22803/castcredits
|
|
||||||
22899;http://api.tvmaze.com/people/22899/castcredits
|
|
||||||
23153;http://api.tvmaze.com/people/23153/castcredits
|
|
||||||
23177;http://api.tvmaze.com/people/23177/castcredits
|
|
||||||
23198;http://api.tvmaze.com/people/23198/castcredits
|
|
||||||
23204;http://api.tvmaze.com/people/23204/castcredits
|
|
||||||
23262;http://api.tvmaze.com/people/23262/castcredits
|
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user