#!/usr/bin/python
# -*- coding: utf-8 -*-
################################
##
## Transmogrifier: ThePlatform
## A Final Cut Server import/export tool
##
## Written by: Beau Hunter beau@318.com
## 318 Inc 05/2009
##
##
## This class is a decedent of TransmogrifierObject, and pretty mimics the
## interface and functionality of it's parent, with one key addition function.
## Once uploaded, YouTube processes the media and XML, and then generates it's
## own XML report as to the status of the import. In order to ensure a successful
## upload, we need to check this file to ensure that if it failed we properly
## report the status. To do so, YouTubeObject provides two additional methods,
## batchStatusCheck() and checkYouTubeXMLStatusFile(). The former method
## actually utilizes the latter, so it will rarely be need to be called directly.
##
## Copyright © 2009-2011 Beau Hunter, 318 Inc.
##
## This file is part of Transmogrifier.
##
## Transmogrifier is free software: you can redistribute it and/or modify
## it under the terms of version 3 the GNU General Public License as published
## by the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## Transmogrifier is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Transmogrifier. If not, see <http://www.gnu.org/licenses/>.
##
#############################################################
import os, os.path, re, glob, shutil, sys, datetime, time
from ftplib import FTP
from fcsxml import FCSXMLField, FCSXMLObject
from transmogrifierTarget import TransmogrifierTargetObject, MediaFile
from decimal import *
from xml.dom import minidom
version = ".91beta"
build = "2010040101"
[docs]class thePlatformObject(TransmogrifierTargetObject):
talent = ""
author = ""
copyright = ""
aspectRatio = ""
team = ""
team2 = ""
region1 = ""
region2 = ""
region3 = ""
region4 = ""
fileName = ""
fileBaseName = ""
conference = ""
airDate = ""
expirationDate = ""
availableDate = ""
uploadPath = ""
movLocation = ""
thumbnailLocation = ""
validActions = ['upload']
debug = False
def __init__(self,entityID=0):
TransmogrifierTargetObject.__init__(self,entityID)
self.talent = ""
self.author = ""
self.copyright = ""
self.team = ""
self.team2 = ""
self.region1 = ""
self.region2 = ""
self.region3 = ""
self.region4 = ""
self.fileName = ""
self.fileBaseName = ""
self.airDate = ""
self.expirationDate = ""
self.availableDate = ""
self.uploadPath = ""
self.movLocation = ""
self.thumbnailLocation = ""
self.serviceName = "thePlatform"
self.supportSubDirs = ["media","thumbnails","xmlin","xmlout"]
self.neededAttributes = ["title","talent","team","airDate","expirationDate"]
self.validActions = ['upload']
[docs] def deleteSupportFiles(self):
"""Delete Registered support files (media and xml)"""
## Call our parent, which removes any loaded media or xml files
if not TransmogrifierTargetObject.deleteSupportFiles(self):
errorCode = 1
## Delete our youtube batch upload file
if self.supportPath:
batchFilePath = os.path.join(self.supportPath, "upload", "%s.batch" % self.entityID)
if os.path.exists(batchFilePath):
self.logger("Removing file at path: '%s'" % batchFilePath, "detailed")
os.remove(batchFilePath)
if errorCode:
return False
else:
return True
[docs] def loadConfiguration(self, parser):
"""Load from configuration file, expects a ConfigParser type object. If you subclass,
you should call this function. If we return false then you should abort. or do your own sanity checks"""
if not TransmogrifierTargetObject.loadConfiguration(self, parser):
return False;
try:
self.ftpHost = parser.get("thePlatform","host")
self.ftpUser = parser.get("thePlatform","username")
self.ftpPassword = parser.get("thePlatform","password")
except:
self.logger("loadConfiguration() Problem loading configuration records, please double check your configuration","error")
return True
[docs] def setFCSXMLFile(self,filePath):
"""import FCS XML file and set relevant member vars"""
self.fcsXMLObject = FCSXMLObject()
if not self.fcsXMLObject.setFile(filePath):
self.logger("Could Not Load FCS XML File: '%s'" % filePath, "error")
return False
self.logger("Loading FCSXML from path: '%s'" % filePath)
mediaSize = self.fcsXMLObject.valueForField("Image Size")
try:
self.frameWidth = re.sub(r'^(\d*?)x(\d*?)$',r'\1',mediaSize)
self.frameHeight = re.sub(r'^(\d*?)x(\d*?)$',r'\2',mediaSize)
aspectValue = Decimal(self.frameWidth) / Decimal(self.frameHeight)
if aspectValue > 1.5:
self.aspectRatio = "16x9"
else:
self.aspectRatio = "4x3"
except:
self.logger("Could not determine mediaSize from XML!","error")
self.entityID = self.fcsXMLObject.entityID
if self.fcsXMLObject.valueForField("File Name"):
self.fileName = self.fcsXMLObject.valueForField("File Name")
self.fileBaseName = os.path.splitext(self.fileName)[0]
if self.fcsXMLObject.valueForField("Talent"):
self.talent = self.fcsXMLObject.valueForField("Talent")
if self.fcsXMLObject.valueForField("Author"):
self.author = self.fcsXMLObject.valueForField("Author")
if self.fcsXMLObject.valueForField("Keywords"):
self.keywordString = self.fcsXMLObject.valueForField("Keywords")
if self.fcsXMLObject.valueForField("Description"):
self.description = self.fcsXMLObject.valueForField("Description")
if self.fcsXMLObject.valueForField("Available Date"):
myDateTime = datetime.datetime.strptime(self.fcsXMLObject.valueForField("Available Date"),"%Y-%m-%dT%H:%M:%SZ")
timeDelta = datetime.timedelta(hours=10)
adjustedDateTime = myDateTime - timeDelta
self.availableDate = adjustedDateTime.strftime("%Y-%m-%dT%H:%M:%S")
if self.fcsXMLObject.valueForField("Expiration Date"):
myDateTime = datetime.datetime.strptime(self.fcsXMLObject.valueForField("Expiration Date"),"%Y-%m-%dT%H:%M:%SZ")
timeDelta = datetime.timedelta(hours=10)
adjustedDateTime = myDateTime - timeDelta
self.expirationDate = adjustedDateTime.strftime("%Y-%m-%dT%H:%M:%S")
if self.fcsXMLObject.valueForField("Air Date"):
myDateTime = datetime.datetime.strptime(self.fcsXMLObject.valueForField("Air Date"),"%Y-%m-%dT%H:%M:%SZ")
timeDelta = datetime.timedelta(hours=10)
adjustedDateTime = myDateTime - timeDelta
self.airDate = adjustedDateTime.strftime("%Y-%m-%dT%H:%M:%S")
if self.fcsXMLObject.valueForField("Upload Path"):
self.uploadPath = self.fcsXMLObject.valueForField("Upload Path")
if self.fcsXMLObject.valueForField("Team"):
self.team = self.fcsXMLObject.valueForField("Team")
if self.fcsXMLObject.valueForField("Team 2"):
self.team2 = self.fcsXMLObject.valueForField("Team 2")
if self.fcsXMLObject.valueForField("Share to Region - 1"):
self.region1 = self.fcsXMLObject.valueForField("Share to Region - 1")
if self.fcsXMLObject.valueForField("Share to Region - 2"):
self.region2 = self.fcsXMLObject.valueForField("Share to Region - 2")
if self.fcsXMLObject.valueForField("Share to Region - 3"):
self.region3 = self.fcsXMLObject.valueForField("Share to Region - 3")
if self.fcsXMLObject.valueForField("Share to Region - 4"):
self.region4 = self.fcsXMLObject.valueForField("Share to Region - 4")
if not self.title:
self.title = self.fcsXMLObject.valueForField("Title")
return True
[docs] def upload(self, dirPath=""):
'''Calls xmlOut and uploads the resulting file'''
self.logger("Uploading XML to thePlatform!")
theError = ""
## Sanity Checks
if not dirPath:
dirPath = self.supportPath
if not os.path.isdir(dirPath):
self.logger("thePlatform_xml:upload() Directory does not exist:'%s'" % dirPath, "error")
return False
datestamp = datetime.datetime.now()
xmlFileName = "%s_%s.xml" % (self.team,datestamp.strftime("%m%d%y"))
xmlOutPath = os.path.join(dirPath,"xmlout",xmlFileName)
if not self.xmlOut(xmlOutPath):
self.logger("upload() could not write XML, exiting", "error")
return False
## Establish our FTP upload command STOR overrides, STOU fails on existing object
if self.overwriteExistingFiles:
ftpCommand = "STOR"
else:
ftpCommand = "STOU"
## Sanity checks and then try our FTP connection
if not self.ftpHost or not self.ftpUser or not self.ftpPassword:
self.logger("upload() missing parameters, could not establish connection to FTP server!", "error")
try:
ftp = FTP(self.ftpHost, self.ftpUser, self.ftpPassword)
except:
self.logger("upload() failed to connect to FTP server", "error")
return False
try:
if os.path.exists(xmlOutPath):
theFile = open(xmlOutPath, "r")
self.logger("upload() uploading file: '%s'" % xmlFileName, "normal")
ftp.storbinary("%s Prod/%s/%s" % (ftpCommand,self.uploadPath,xmlFileName), theFile)
theFile.close()
else:
self.logger("upload() failed to upload file: 'manifest.xml'", "error")
theError = self.lastError
except:
theError = xmlOutPath,sys.exc_info()[0]
self.logger("upload() could not upload file: '%s' Error:\n%s" % (theError), "error")
## Build our FCS object for reporting
if not self.fcsXMLOutObject:
self.fcsXMLOutObject = FCSXMLObject(self.entityID)
fcsXMLOut = self.fcsXMLOutObject
currentTime = datetime.datetime.fromtimestamp(time.mktime(datetime.datetime.now().timetuple()))
if not theError:
fcsXMLOut.setField(FCSXMLField("Published to %s" % self.serviceName, "true", "bool"))
fcsXMLOut.setField(FCSXMLField("Publish to %s" % self.serviceName, "false", "bool"))
fcsXMLOut.setField(FCSXMLField("Status", "Verify Publishing"))
self.appendFCSField("ThePlatform Publish History","%s: Successfully published to %s." % (currentTime,self.serviceName))
self.logger("Successfully published assets to %s." % (self.serviceName))
return True
else:
self.appendHistory("%s: Failed to publish to %s. Please try again. Error:\n\t%s" % (currentTime,self.serviceName,self.lastError))
fcsXMLOut.setField(FCSXMLField("Publish to %s" % self.serviceName, "false", "bool"))
self.logger("Failed to publish all assets to %s." % (self.serviceName), "error")
return False
[docs] def xmlOut(self, filePath=""):
'''Output our thePlatform compliant XML. If passed a filepath for the second
Parameter, we write to that file, otherwise we print to stdout'''
self.logger("Writing XML file to path: %s" % filePath)
## Sanity checks and variable initialization
## Set our variables
title = self.title
talent = self.talent
description = self.description
author = self.author
aspectRatio = self.aspectRatio
keywords = self.keywordString
airDate = self.airDate
expirationDate = self.expirationDate
availableDate = self.availableDate
uploadPath = self.uploadPath
filename = self.fileName
fileBaseName = self.fileBaseName
## Determine our categories
rawCategories = [ self.team ]
categories = []
for category in rawCategories:
if category and not category == "Unspecified":
categories.append(category)
movLocation = "file://172.16.130.22/MOV_Drop/%s_MOV/%s.mov" % (uploadPath,fileBaseName)
thumbnailLocation = "file://172.16.130.22/JPEG_Drop/%s_JPEG/%s.jpg" % (uploadPath,fileBaseName)
## Make sure either that our xml out file doesn't already exist, or
## that we're allowed to overwrite existing files. Then Generate our XML
if (filePath and (not os.path.exists(filePath) \
or (os.path.exists(filePath) and self.overwriteExistingFiles))
and os.path.isdir(os.path.dirname(filePath))) \
or not filePath :
## <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
## <addContent>
## <media>
## <title>SEASON 2 TEASER</title>
## <customData>
## <CustomDataElement>
## <title>Talent</title>
## <value/>
## </CustomDataElement>
## <CustomDataElement>
## <title>AspectRatio</title>
## <value>16x9</value>
## </CustomDataElement>
## </customData>
## <airdate>2010-06-04T00:00:00.000</airdate>
## <availableDate>2010-06-04T00:00:00.000</availableDate>
## <expirationDate>2011-06-04T00:00:00.000</expirationDate>
## <author>Sarah Fowler</author>
## <categories>
## <string>Gatorade</string>
## </categories>
## <description>MyDescription</description>
## <keywords>Gatorade, Replay</keywords>
## </media>
## <mediaFiles>
## <mediaFile>
## <assetTypes>
## <string>video</string>
## </assetTypes>
## <contentType>Video</contentType>
## <encodingProfileTitle>Mezzanine Video</encodingProfileTitle>
## <originalLocation>file://172.16.130.22/MOV_Drop/Microsite_MOV/replay_season2_teaser_r2_063010.mov</originalLocation>
## </mediaFile>
## <mediaFile>
## <assetTypes>
## <string>thumbnail</string>
## </assetTypes>
## <contentType>Image</contentType>
## <encodingProfileTitle>Mezzanine Thumbnail</encodingProfileTitle>
## <originalLocation>file://172.16.130.22/JPEG_Drop/Microsite_JPEG/replay_season2_teaser_r2_063010.jpg</originalLocation>
## </mediaFile>
## </mediaFiles>
## </addContent>
## Start XML Creation
## <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
## <addContent>
## <media>
xmlObject = minidom.Document()
xmlDoc = xmlObject
addContentElement = xmlDoc.createElement("addContent")
xmlDoc.appendChild(addContentElement)
mediaElement = xmlDoc.createElement("media")
addContentElement.appendChild(mediaElement)
## <title>SEASON 2 TEASER</title>
titleElement = xmlDoc.createElement("title")
titleElement.appendChild(xmlDoc.createTextNode(title))
mediaElement.appendChild(titleElement)
## If talent is set, add a CustomDataElement node for it.
## <CustomDataElement>
## <title>Talent</title>
## <value>Karl Malone</value>
## </CustomDataElement>
customDataElement = xmlDoc.createElement("customData")
mediaElement.appendChild(customDataElement)
if talent:
customDataTalentElement = xmlDoc.createElement("CustomDataElement")
customDataTalentTitle = xmlDoc.createElement("title")
customDataTalentTitle.appendChild(xmlDoc.createTextNode("Talent"))
customDataTalentElement.appendChild(customDataTalentTitle)
customDataTalentValue = xmlDoc.createElement("value")
customDataTalentValue.appendChild(xmlDoc.createTextNode(talent))
customDataTalentElement.appendChild(customDataTalentValue)
customDataElement.appendChild(customDataTalentElement)
## Add CustomDataElement node for our Aspect Ration
## <CustomDataElement>
## <title>AspectRatio</title>
## <value>16x9</value>
## </CustomDataElement>
## </customData>
customDataAspectRatioElement = xmlDoc.createElement("CustomDataElement")
customDataAspectRatioTitle = xmlDoc.createElement("title")
customDataAspectRatioTitle.appendChild(xmlDoc.createTextNode("AspectRatio"))
customDataAspectRatioElement.appendChild(customDataAspectRatioTitle)
customDataAspectRatioValue = xmlDoc.createElement("value")
customDataAspectRatioValue.appendChild(xmlDoc.createTextNode(aspectRatio))
customDataAspectRatioElement.appendChild(customDataAspectRatioValue)
customDataElement.appendChild(customDataAspectRatioElement)
## <airdate>2010-06-04T00:00:00.000</airdate>
airdateElement = xmlDoc.createElement("airdate")
airdateElement.appendChild(xmlDoc.createTextNode(airDate))
mediaElement.appendChild(airdateElement)
## <availableDate>2010-06-04T00:00:00.000</availableDate>
availableDateElement = xmlDoc.createElement("availableDate")
availableDateElement.appendChild(xmlDoc.createTextNode(availableDate))
mediaElement.appendChild(availableDateElement)
## <expirationDate>2011-06-04T00:00:00.000</expirationDate>
expirationDateElement = xmlDoc.createElement("expirationDate")
expirationDateElement.appendChild(xmlDoc.createTextNode(expirationDate))
mediaElement.appendChild(expirationDateElement)
## <author>Sarah Fowler</author>
authorElement = xmlDoc.createElement("author")
authorElement.appendChild(xmlDoc.createTextNode(author))
mediaElement.appendChild(authorElement)
## <categories>
## <string>Gatorade</string>
## </categories>
if categories and len(categories) > 0:
categoriesElement = xmlDoc.createElement("categories")
for category in categories:
categoryElement = xmlDoc.createElement("string")
categoryElement.appendChild(xmlDoc.createTextNode(category))
categoriesElement.appendChild(categoryElement)
mediaElement.appendChild(categoriesElement)
## <description>MyDescription</description>
descriptionElement = xmlDoc.createElement("description")
descriptionElement.appendChild(xmlDoc.createTextNode(description))
mediaElement.appendChild(descriptionElement)
## <keywords>Gatorade, Replay</keywords>
keywordsElement = xmlDoc.createElement("keywords")
keywordsElement.appendChild(xmlDoc.createTextNode(keywords))
mediaElement.appendChild(keywordsElement)
#### Process our media files
## <mediaFiles>
mediaFilesElement = xmlDoc.createElement("mediaFiles")
addContentElement.appendChild(mediaFilesElement)
#### Process our video file
## <mediaFile>
## <assetTypes>
## <string>video</string>
## </assetTypes>
## <contentType>Video</contentType>
## <encodingProfileTitle>Mezzanine Video</encodingProfileTitle>
## <originalLocation>file://172.16.130.22/MOV_Drop/Microsite_MOV/replay_season2_teaser_r2_063010.mov</originalLocation>
## </mediaFile>mediaFileElement = xmlDoc.createElement("mediaFile")
mediaFileElement = xmlDoc.createElement("mediaFile")
mediaFilesElement.appendChild(mediaFileElement)
assetTypesElement = xmlDoc.createElement("assetTypes")
stringElement = xmlDoc.createElement("string")
stringElement.appendChild(xmlDoc.createTextNode("video"))
assetTypesElement.appendChild(stringElement)
mediaFileElement.appendChild(assetTypesElement)
contentTypeElement = xmlDoc.createElement("contentType")
contentTypeElement.appendChild(xmlDoc.createTextNode("Video"))
mediaFileElement.appendChild(contentTypeElement)
encodingProfileTitleElement = xmlDoc.createElement("encodingProfileTitle")
encodingProfileTitleElement.appendChild(xmlDoc.createTextNode("Mezzanine Video"))
mediaFileElement.appendChild(encodingProfileTitleElement)
originalLocationElement = xmlDoc.createElement("originalLocation")
originalLocationElement.appendChild(xmlDoc.createTextNode(movLocation))
mediaFileElement.appendChild(originalLocationElement)
#### Process our thumbnail file
mediaFileElement = xmlDoc.createElement("mediaFile")
mediaFilesElement.appendChild(mediaFileElement)
assetTypesElement = xmlDoc.createElement("assetTypes")
stringElement = xmlDoc.createElement("string")
stringElement.appendChild(xmlDoc.createTextNode("thumbnail"))
assetTypesElement.appendChild(stringElement)
mediaFileElement.appendChild(assetTypesElement)
contentTypeElement = xmlDoc.createElement("contentType")
contentTypeElement.appendChild(xmlDoc.createTextNode("Image"))
mediaFileElement.appendChild(contentTypeElement)
encodingProfileTitleElement = xmlDoc.createElement("encodingProfileTitle")
encodingProfileTitleElement.appendChild(xmlDoc.createTextNode("Mezzanine Thumbnail"))
mediaFileElement.appendChild(encodingProfileTitleElement)
originalLocationElement = xmlDoc.createElement("originalLocation")
originalLocationElement.appendChild(xmlDoc.createTextNode(thumbnailLocation))
mediaFileElement.appendChild(originalLocationElement)
## Write our XML to file if provided, otherwise echo it
if filePath:
theFile = open(filePath, "w")
xmlDoc.writexml(theFile)
theFile.close()
else:
print xmlDoc.toprettyxml()
elif os.path.exists(filePath) and not self.overwriteExistingFiles:
self.logger("File already exists at path: %s, exiting!" % filePath, "error")
return False
elif not os.path.exists(os.path.dirname(filePath)):
self.logger("Directory does not exist at path: %s, exiting!" % os.path.dirname(filePath), "error")
return false
else:
self.logger("Uncaught Exception: Error writing XML", "error")
return False
xmlDoc.unlink()
return True