# -*- coding: utf-8 -*- # # File: engage.py # # Copyright (c) 2006 by Nate Aune (Jazkarta), Andy Nicholson, Wolfgang Reutz # Generator: ArchGenXML Version 1.5.0 svn/devel # http://plone.org/products/archgenxml # # GNU General Public License (GPL) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. # __author__ = """Nate Aune (Jazkarta) , Christof Haemmerle (nex9) , Chris Miles , Andy Nicholson , Wolfgang Reutz , Dave Fregon """ __docformat__ = 'plaintext' from AccessControl import ClassSecurityInfo from Products.Archetypes.atapi import * from Products.ATVideo.content.video import ATVideo from Products.ATVocabularyManager.namedvocabulary import NamedVocabulary from Products.ATVideo.config import * from Products.ATVideo.interfaces.content_markers import IEngageVideo from zope.interface import implements # additional imports from tagged value 'import' from Products.ATCountryWidget.Widget import CountryWidget ##code-section module-header #fill in your manual code here #Tags # XXX doesnt work! #from Products.Tags.Widget import TagWidget from Products.ATVideo.Widgets import ContentLicensingWidget, TimeDurationWidget, TagKeywordWidget from Products.CMFCore.utils import getToolByName from Products.CMFCore.WorkflowCore import WorkflowException from Products.CMFCore.permissions import View import zLOG import traceback,os from xml.sax import saxutils import md5 from zope.event import notify from zope.app.event.objectevent import ObjectModifiedEvent ##/code-section module-header schema = Schema(( StringField( name='Producer', widget=StringWidget( label="Producer", label_msgid='ATVideo_label_Producer', i18n_domain='ATVideo', ), languageIndependent=1, schemata="step 1", searchable=True ), StringField( name='Director', widget=StringWidget( label="Director", label_msgid='ATVideo_label_Director', i18n_domain='ATVideo', ), languageIndependent=1, schemata="step 1", searchable=True ), StringField( name='ProducerEmailAddress', widget=StringWidget( label="Producer's email address", label_msgid='ATVideo_label_ProducerEmailAddress', i18n_domain='ATVideo', ), languageIndependent=1, schemata="step 1" ), StringField( name='ProducerMailingAddress', widget=StringWidget( label="Producer's address details", label_msgid='ATVideo_label_ProducerMailingAddress', i18n_domain='ATVideo', ), languageIndependent=1, schemata="step 1" ), StringField( name='projectName', widget=StringWidget( label="Project Name", label_msgid='ATVideo_label_projectName', i18n_domain='ATVideo', ), languageIndependent=1, schemata="step 1", searchable=True ), StringField( name='productionCompanyName', widget=StringWidget( label="Production Company", label_msgid='ATVideo_label_productionCompanyName', i18n_domain='ATVideo', ), languageIndependent=1, schemata="step 1", searchable=True ), StringField( name='websiteAddress', widget=StringWidget( label="Website address", label_msgid='ATVideo_label_websiteAddress', i18n_domain='ATVideo', ), languageIndependent=1, schemata="step 1" ), StringField( name='Distributor', widget=StringWidget( label="Distributor", label_msgid='ATVideo_label_Distributor', i18n_domain='ATVideo', ), languageIndependent=1, schemata="step 1", searchable=True ), TextField( name='FullDescription', allowable_content_types=('text/plain', 'text/structured', 'text/html', 'application/msword',), widget=RichWidget( label="Full Description of your film", label_msgid='ATVideo_label_FullDescription', i18n_domain='ATVideo', ), languageIndependent=1, schemata="step 1", searchable=True, default_output_type='text/plain' ), DateTimeField( name='DateProduced', widget=CalendarWidget( label="Date produced", label_msgid='ATVideo_label_DateProduced', i18n_domain='ATVideo', ), languageIndependent=1, schemata="step 1" ), StringField( name='Country', widget=CountryWidget( label="Country", label_msgid='ATVideo_label_Country', i18n_domain='ATVideo', ), languageIndependent=1, schemata="step 2", accessor="getCountry" ), StringField( name='Genre', widget=SelectionWidget( label="Genre", label_msgid='ATVideo_label_Genre', i18n_domain='ATVideo', ), languageIndependent=1, schemata="step 2", vocabulary=NamedVocabulary("""video_genre"""), accessor="getGenre" ), LinesField( name='Categories', widget=MultiSelectionWidget( label="Topics. Hold down CTRL/COMMAND and click to multiple select topics.", label_msgid='ATVideo_label_Categories', i18n_domain='ATVideo', size=10, ), languageIndependent=1, multiValued=1, vocabulary=NamedVocabulary("""video_categories"""), schemata="step 2", accessor="getCategories", ), #StringField( # name='Tags', # widget=StringWidget( # label="Tags", # label_msgid='ATVideo_label_Tags', # i18n_domain='ATVideo', # ), # languageIndependent=1, # schemata="step 2" #), ## These following three fields are DEPRECATED # They are not being used, but leave in , for backwards compatiablity. StringField( name='DurationOfVideoHour', widget=SelectionWidget( label="Duration (Hours)", label_msgid='ATVideo_label_DurationOfVideoHour', i18n_domain='ATVideo', ), languageIndependent= 1, schemata="step 3", vocabulary= ['0','1','2','3','4'], ), #not used - see above StringField( name='DurationOfVideoMin', widget=SelectionWidget( label="Duration (Minutes)", label_msgid='ATVideo_label_DurationOfVideoMin', i18n_domain='ATVideo', ), languageIndependent=1, schemata="step 3", vocabulary= range(0, 60), ), #not used - see above StringField( name='DurationOfVideoSecs', widget=SelectionWidget( label="Duration (Seconds)", label_msgid='ATVideo_label_DurationOfVideoSecs', i18n_domain='ATVideo', ), languageIndependent= 1, schemata="step 3", vocabulary= range(0, 60), ), StringField( name='ThumbnailImageDescription', widget=StringWidget( label="Description of thumbnail image", label_msgid='ATVideo_label_ThumbnailImageDescription', i18n_domain='ATVideo', ), languageIndependent=1, schemata="step 3" ), StringField( name='contentlicensingString', widget=ContentLicensingWidget( label="License", label_msgid='ATVideo_label_contentlicensingString', i18n_domain='ATVideo', ), languageIndependent=1, schemata="step 2" ), LinesField( name='subject', widget=TagKeywordWidget( label="Tags. Additional terms describing your video eg: \"APEC jakarta quirky\" - **one word per line**", label_msgid='ATVideo_label_subject', i18n_domain='ATVideo', ), accessor="Subject", searchable=True, multiValued=1, schemata="step 2" ), # This is the field that is in current use, for the custom video duration widget # StringField( name='DurationOfVideo', widget=TimeDurationWidget( label="Duration Of Video", label_msgid='ATVideo_label_DurationOfVideo', i18n_domain='ATVideo', ), languageIndependent= 1, schemata="step 3", required=True, validators = ('isTimeType'), index="FieldIndex:schema", index_method="getFormattedDuration" ), ), ) ##code-section after-local-schema #fill in your manual code here ##/code-section after-local-schema ATEngageVideo_schema = BaseSchema.copy() + \ getattr(ATVideo, 'schema', Schema(())).copy() + \ schema.copy() ##code-section after-schema #fill in your manual code here #stage 1 tab # # Change 'title', 'description', 'id', and others, schema fields to use schemata='step 1' ATEngageVideo_schema['title'].schemata = "step 1" ATEngageVideo_schema['description'].schemata = "step 1" ATEngageVideo_schema['id'].schemata = "step 1" ATEngageVideo_schema['allowDiscussion'].schemata = "step 1" ATEngageVideo_schema['relatedItems'].schemata = "step 1" ATEngageVideo_schema['description'].required = True ATEngageVideo_schema.moveField('DateProduced',before='FullDescription') ATEngageVideo_schema['DateProduced'].widget.show_hm = False ATEngageVideo_schema['DateProduced'].required = True #schema.moveField('relatedItems', pos='bottom') ATEngageVideo_schema['relatedItems'].widget.visible['edit'] = 'invisible' ATEngageVideo_schema['allowDiscussion'].widget.visible['edit'] = 'invisible' #stage 2 tab ATEngageVideo_schema.moveField('subject', after='Categories') #stage 3 tab #make the file widget appear on the third tab #this field comes from ATMediaFile ATEngageVideo_schema['file'].schemata = "step 3" ATEngageVideo_schema['file'].widget.label="Video File" ATEngageVideo_schema.moveField('DurationOfVideo',after='file') #migration code ATEngageVideo_schema['DurationOfVideoHour'].widget.visible['edit'] = 'invisible' ATEngageVideo_schema['DurationOfVideoMin'].widget.visible['edit'] = 'invisible' ATEngageVideo_schema['DurationOfVideoSecs'].widget.visible['edit'] = 'invisible' ##/code-section after-schema class ATEngageVideo(ATVideo): """ """ security = ClassSecurityInfo() __implements__ = (getattr(ATVideo,'__implements__',()),) implements( IEngageVideo ) # This name appears in the 'add' box archetype_name = 'ATEngageVideo' meta_type = 'ATEngageVideo' portal_type = 'ATEngageVideo' allowed_content_types = [] + list(getattr(ATVideo, 'allowed_content_types', [])) filter_content_types = 0 global_allow = 1 allow_discussion = True #content_icon = 'ATEngageVideo.gif' immediate_view = 'engage_video_view' default_view = 'engage_video_view' suppl_views = () typeDescription = "ATEngageVideo" typeDescMsgId = 'description_edit_atengagevideo' _at_rename_after_creation = True schema = ATEngageVideo_schema ##code-section class-header #fill in your manual code here security.declareProtected(View, 'index_html') def index_html(self, REQUEST=None, RESPONSE=None): """We override ATVideo's behaviour, If there is a downloadhost, redirect via that else revert to ATVideo.index_html """ if self.isUsingRemoteServer(): return RESPONSE.redirect( self.getDownloadServerURL() ) else: ATVideo.index_html(self,REQUEST,RESPONSE) def processForm(self, data=1, metadata=0, REQUEST=None, values=None): """Process the schema looking for data in the form""" #call up ATVideo.processForm(self,data,metadata,REQUEST,values) #do 'submit' workflow action #make sure we arent on next or previous request = REQUEST or self.REQUEST if values: form = values else: form = request.form next = not form.get('form_next', None) is None previous = not form.get('form_previous', None) is None save = not form.get('form_submit', None) is None fieldset = form.get('fieldset',None) zLOG.LOG('ATEngageVideo', zLOG.INFO, 'Doing workflow action? next:%s previous:%s save:%s fieldset:%s' % (next, previous, save, fieldset)) print "checking if we pressed save on step 3" if save and fieldset=='step 3': zLOG.LOG('ATEngageVideo', zLOG.INFO, 'Doing submit , if not published already') try: workflow = getToolByName(self, 'portal_workflow') state = workflow.getInfoFor(self,'review_state') zLOG.LOG('ATEngageVideo',zLOG.INFO,'In workflow state %s'%state) #dont try to resubmit if already published. if not state == 'published': workflow.doActionFor(self, 'submit') except WorkflowException: traceback.print_exc() pass security.declarePublic('notifyModifiedEvent') def notifyModifiedEvent(self): """ Reindexes the video object, and calls notifiy(ObjectModifiedEvent(self)) """ #reindex the object self.reindexObject() #send an event, IObjectModifiedEvent #will be picked up by the subscriber in PlumiSkin at least notify(ObjectModifiedEvent(self)) # Methods security.declarePublic('getGenre') def getGenre(self): """ """ vocab_key = schema['Genre'].get(self) return vocab_key security.declarePublic('getCategories') def getCategories(self): """ """ vocab_key = schema['Categories'].get(self) return vocab_key security.declarePublic('getCountries') def getCountries(self): """ """ vocab_key = schema['Country'].get(self) return vocab_key # Manually created methods security.declarePublic('getFirstPublishedTransitionTime') def getFirstPublishedTransitionTime(self): #trawls review_history try: history=self.portal_workflow.getInfoFor(self,'review_history') except WorkflowException: return None #find the first 'publish' transition first_time_published = None for h in history: if h['action'] == 'publish' and h['review_state'] == 'published': first_time_published = h['time'] break return first_time_published security.declarePublic('getWebsiteURL') def getWebsiteURL(self): #guarantees a correctly formatted URL from user data user_addr = schema['websiteAddress'].get(self) if not user_addr[:7]=='http://': return 'http://'+user_addr else: return user_addr security.declarePublic('str_format') def str_format(self,t,s, show_zero=False): str = '' try: if int(t) > 1: str = '%s %ss' % (t,s) else: if int(t) == 1: str = '%s %s' % (t,s) else: if show_zero: str = '0 %ss' % s except: str = 'error' return str security.declarePublic('getOldFormattedDuration') def getOldFormattedDuration(self): #the old method #only for migration hr=schema['DurationOfVideoHour'].get(self) mi=schema['DurationOfVideoMin'].get(self) se=schema['DurationOfVideoSecs'].get(self) return self.str_format(hr,'hour') + ' ' + self.str_format(mi,'minute',True) + ' ' + self.str_format(se,'second',True) security.declarePublic('migrateDuration') def migrateDuration(self): #set the new style duration schema field #used for migration hr=schema['DurationOfVideoHour'].get(self) mi=schema['DurationOfVideoMin'].get(self) se=schema['DurationOfVideoSecs'].get(self) if hr != None and mi != None and se != None: str = hr+":"+mi+":"+se schema['DurationOfVideo'].set(self, str) security.declarePublic('getFormattedDuration') def getFormattedDuration(self): try: time_val=schema['DurationOfVideo'].get(self) except: #the old method #only for migration self.getOldFormattedDuration() try: hr = int(time_val.split(":")[0]) except: hr = 0 try: mi = int(time_val.split(":")[1]) except: mi = 0 try: se = int(time_val.split(":")[2]) except: se = 0 return self.str_format(hr,'hour') + ' ' + self.str_format(mi,'minute',True) + ' ' + self.str_format(se,'second',True) security.declarePublic('getTorrentURL') def getTorrentURL(self): #get the BiTTorrentFile related to this object objs = self.getRelatedItems() for v in objs: #test v is a BitTorrentFile, use the first one we find zLOG.LOG('ATEngageVideo', zLOG.INFO,'getTorrentURL: found related item %s' % v.getTypeInfo().getId()) if v.getTypeInfo().getId()=='BitTorrentFile': return v.absolute_url() return '' security.declarePublic('getIndyTubeHTML') def getIndyTubeHTML(self): #open the file with ".inc" on the end of the name of the object, if it exists # get the ExternalStorage object for the video file file_ext_storage_obj = self.Schema()['file'].storage # get the filepath # ATMediaFile (and ATVideo) by default uses ExternalStorage # this file path is the path to the var/files/ ext storage area # inside the zope instance fn_ext_storage = file_ext_storage_obj.getFilepath(self,'file') #make the flv inc file name #make the hexdigest of the full filename (not the path) eg "mymovie.avi" hash=md5.new(os.path.basename(fn_ext_storage)) stem = fn_ext_storage.rsplit(".",1)[0] + "-" + hash.hexdigest() fpath = stem + ".flv.inc" zLOG.LOG('ATEngageVideo',zLOG.INFO,'getIndyTubeHTML using %s'%fpath) try: fileobj = open(fpath,"r") return fileobj.read() #return "filename %s " % (fpath) except: return "" security.declarePublic('getIndyTubeHTMLMarkedUp') def getIndyTubeHTMLMarkedUp(self): # # html = self.getIndyTubeHTML() html_esc = saxutils.escape(html) return html_esc security.declarePublic('getIndyTubeDownloadURL') def getIndyTubeDownloadURL(self): #ATM we just assume , if indytube processing worked #then we can access an ogg file for downloading return 'not-implemented' security.declarePublic('stopIndyTube') def stopIndyTube(self): """Delete the current .flv.inc file, and write out a skip file, to make indytube STOP re-encoding.""" #open the file with ".inc" on the end of the name of the object, if it exists # get the ExternalStorage object for the video file file_ext_storage_obj = self.Schema()['file'].storage # get the filepath # ATMediaFile (and ATVideo) by default uses ExternalStorage # this file path is the path to the var/files/ ext storage area # inside the zope instance fn_ext_storage = file_ext_storage_obj.getFilepath(self,'file') #make the flv inc file name #make the hexdigest of the full filename (not the path) eg "mymovie.avi" hash=md5.new(os.path.basename(fn_ext_storage)) stem = fn_ext_storage.rsplit(".",1)[0] + "-" + hash.hexdigest() fpath = stem + ".flv.inc" fpath_skip = stem +".wetube_skip" template_error = False try: os.unlink(fpath) except: #its an error if the template doesnt delete template_error = True try: #make a skip file os.mknod(fpath_skip) except: #this is an error too template_error = True pass # #return any errors if template_error: return None return True security.declarePublic('reencodeIndyTube') def reencodeIndyTube(self): """Delete the current .flv.inc file, to make indytube re-encode. Return None if the template doesnt delete, or else the template file name""" #open the file with ".inc" on the end of the name of the object, if it exists # get the ExternalStorage object for the video file file_ext_storage_obj = self.Schema()['file'].storage # get the filepath # ATMediaFile (and ATVideo) by default uses ExternalStorage # this file path is the path to the var/files/ ext storage area # inside the zope instance fn_ext_storage = file_ext_storage_obj.getFilepath(self,'file') #make the flv inc file name #make the hexdigest of the full filename (not the path) eg "mymovie.avi" hash=md5.new(os.path.basename(fn_ext_storage)) stem = fn_ext_storage.rsplit(".",1)[0] + "-" + hash.hexdigest() fpath = stem + ".flv.inc" fpath_lock = stem +".wetube_lock" fpath_skip = stem +".wetube_skip" template_error = False try: os.unlink(fpath) except: #its an error if the template doesnt delete template_error = True try: os.unlink(fpath_lock) except: zLOG.LOG('ATEngageVideo',zLOG.INFO,'reencodeIndyTube caught an error deleting lock file %s ' % (fpath_lock)) try: os.unlink(fpath_skip) except: zLOG.LOG('ATEngageVideo',zLOG.INFO,'reencodeIndyTube caught an error deleting skip file %s ' % (fpath_skip)) if template_error: return None return fpath security.declarePublic('getIndyTubeStatus') def getIndyTubeStatus(self): """Returns the status of indytube transcoding""" #open the file with ".inc" on the end of the name of the object, if it exists # get the ExternalStorage object for the video file file_ext_storage_obj = self.Schema()['file'].storage # get the filepath # ATMediaFile (and ATVideo) by default uses ExternalStorage # this file path is the path to the var/files/ ext storage area # inside the zope instance fn_ext_storage = file_ext_storage_obj.getFilepath(self,'file') #make the flv inc file name #make the hexdigest of the full filename (not the path) eg "mymovie.avi" hash=md5.new(os.path.basename(fn_ext_storage)) stem = fn_ext_storage.rsplit(".",1)[0] + "-" + hash.hexdigest() fpath = stem + ".flv.inc" fpath_lock = stem +".wetube_lock" fpath_skip = stem +".wetube_skip" #error if (os.path.exists(fpath_skip)): return -1 #transcoding started if (os.path.exists(fpath_lock)): return 0 #transcoded finished and successful if (os.path.exists(fpath)): return 1 #waiting for transcoding return 2 def createTorrent(self): #call ATVideo createTorrent method ATVideo.createTorrent(self) #check we have a valid torrent file. if self.getTorrentURL() != '': zLOG.LOG('ATEngageVideo',zLOG.INFO,'Ok , create torrent succeeded ') else: #if we have enabled either local or remote bittorrent support, and the url is blank, it failed. if (self.portal_atmediafiletool.getEnable_remote_bittorrent() or self.portal_atmediafiletool.getEnable_bittorrent()): zLOG.LOG('ATEngageVideo',zLOG.INFO,'.torrent creation didnt work') def getGenreFolder(self): return GENRE_FOLDER def getCategoriesFolder(self): return CATEGORIES_FOLDER def getTopLevelFolder(self): return TOPLEVEL_FOLDER def getCountryFolder(self): return COUNTRIES_FOLDER def getSyndicationManager(self): return SYN_MANAGER ##/code-section class-header registerType(ATEngageVideo, PROJECTNAME) # end of class ATEngageVideo ##code-section module-footer #fill in your manual code here ##/code-section module-footer