├── README.md └── pyCast.py /README.md: -------------------------------------------------------------------------------- 1 | # pycast 2 | 3 | Welcome to pyCast, a simple Python script to generate XML files for serving podcasts. 4 | 5 | To generate files: 6 | 7 | ./pyCast.py -d ~/Public/Podcast -w http://172.31.2.250/Podcast 8 | 9 | This will search all sub-directories under ~/Public/Podcast, and generate an XMl file named after each directory found. 10 | 11 | The base URL for the files will be http://172.31.2.250/Podcast. In this instance, the directory ~/Public was being served by Python ( sudo python -m SimpleHTTPServer 80 ) 12 | 13 | Testing has done on 10.10 / 10.11 using the pre-installed Python -------------------------------------------------------------------------------- /pyCast.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import print_function 4 | import os, sys, getopt, mimetypes, datetime, urllib 5 | import xml.etree.cElementTree as ET 6 | 7 | from Foundation import * 8 | 9 | 10 | attrKeys = { 'kMDItemAlbum': 'Album', 'kMDItemDurationSeconds': 'Duration', 'kMDItemDisplayName': 'Title', 'kMDItemContentCreationDate': 'PubDate', 11 | 'kMDItemLogicalSize': 'Size', 'kMDItemComment': 'Summary', 'kMDItemAuthors': 'Authors' } 12 | 13 | def getMeta( fileName ): 14 | fileDets = {} 15 | if os.path.exists( fileName ): 16 | mdRef = NSURL.fileURLWithPath_( fileName ) 17 | mdItem = NSMetadataItem.alloc().initWithURL_( mdRef ) 18 | for indivKey in attrKeys.keys(): 19 | fileDets[ attrKeys[ indivKey ] ] = mdItem.valueForAttribute_( indivKey ) 20 | 21 | return fileDets 22 | 23 | def genFeed( srcDir ): 24 | for fileFound in os.listdir( srcDir ): 25 | if fileFound.startswith( '.' ): 26 | continue 27 | yield fileFound 28 | 29 | def main( argv = None ): 30 | if argv is None: 31 | argv = sys.argv 32 | 33 | optList, optArgs = getopt.getopt( argv[ 1: ], "w:d:h", [ '--web', '--dir', '--help' ] ) 34 | webBase = None 35 | dirBase = None 36 | for clList, clArgs in optList: 37 | if clList in [ '-w', '--web' ]: 38 | webBase = clArgs 39 | elif clList in [ '-d', '--dir' ]: 40 | dirBase = clArgs 41 | 42 | if dirBase is None: 43 | print( 'Base Dir Not Specified!' ) 44 | return 1 45 | 46 | for srcDir in os.listdir( dirBase ): 47 | if os.path.isfile( os.path.join( dirBase, srcDir ) ): 48 | continue 49 | 50 | rssRoot = ET.Element( "rss" ) 51 | rssChann = ET.SubElement( rssRoot, 'channel' ) 52 | rssChann.attrib[ 'xmlns:itunes' ] = 'http://www.itunes.com/dtds/podcast-1.0.dtd' 53 | rssChann.attrib[ 'version' ] = '2.0' 54 | 55 | castTitle = ET.SubElement( rssChann, 'title' ) 56 | castLink = ET.SubElement( rssChann, 'link' ) 57 | castDesc = ET.SubElement( rssChann, 'description' ) 58 | castiTunesDesc = ET.SubElement( rssChann, 'itunes:summary' ) 59 | castiTunesArtist = ET.SubElement( rssChann, 'itunes:author' ) 60 | 61 | fullPath = os.path.join( dirBase, srcDir ) 62 | for fileName in genFeed( fullPath ): 63 | relPath = os.path.join( srcDir, fileName ) 64 | fullURL = os.path.join( webBase, urllib.quote( relPath ) ) 65 | metaInfo = getMeta( os.path.join( fullPath, fileName ) ) 66 | if metaInfo[ 'Title' ] == '': 67 | metaInfo[ 'Title' ] = srcDir 68 | 69 | castTitle.text = metaInfo[ 'Album' ] 70 | castLink.text = webBase 71 | castDesc.text = metaInfo[ 'Summary' ] 72 | castiTunesDesc.text = metaInfo[ 'Summary' ] 73 | castiTunesArtist.text = str( metaInfo[ 'Authors' ] ) 74 | 75 | castItem = ET.SubElement( rssChann, 'item' ) 76 | 77 | itemTitle = ET.SubElement( castItem, 'title' ) 78 | itemTitle.text = metaInfo[ 'Title' ] 79 | 80 | itemEnc = ET.SubElement( castItem, 'enclosure' ) 81 | itemEnc.attrib[ 'url' ] = str( fullURL ) 82 | itemEnc.attrib[ 'length' ] = str( metaInfo[ 'Size' ] ) 83 | mimeType = mimetypes.guess_type( fullURL )[ 0 ] 84 | if not mimeType is None: 85 | itemEnc.attrib[ 'type' ] = mimetypes.guess_type( fullURL )[ 0 ] 86 | else: 87 | if fileName.endswith( 'm4b' ): 88 | itemEnc.attrib[ 'type' ] = 'audio/x-m4b' 89 | 90 | itemPubDate = ET.SubElement( castItem, 'pubDate' ) 91 | itemPubDate.text = str( metaInfo[ 'PubDate' ] ) 92 | 93 | itemGUID = ET.SubElement( castItem, 'guid' ) 94 | itemGUID.text = fullURL 95 | 96 | itemiTunesAuthor = ET.SubElement( castItem, 'itunes:author' ) 97 | itemiTunesAuthor.text = str( metaInfo[ 'Authors' ] ) 98 | 99 | itemiTunesSummary = ET.SubElement( castItem, 'itunes:summary' ) 100 | itemiTunesSummary.text = str( metaInfo[ 'Summary' ] ) 101 | 102 | itemiTunesSubtitle = ET.SubElement( castItem, 'itunes:subtitle' ) 103 | itemiTunesSubtitle.text = str( metaInfo[ 'Summary' ] ) 104 | 105 | itemiTunesDuration = ET.SubElement( castItem, 'itunes:duration' ) 106 | stringDuration = str( datetime.timedelta( seconds=int( metaInfo[ 'Duration' ] ) ) ) 107 | itemiTunesDuration.text = stringDuration 108 | 109 | fullXML = ET.ElementTree( rssRoot ) 110 | ET.dump( fullXML ) 111 | xmlPath = os.path.join( dirBase, '%s.xml' % srcDir ) 112 | fullXML.write ( xmlPath ) 113 | 114 | if __name__ == '__main__': 115 | sys.exit( main() ) 116 | 117 | 118 | 119 | 120 | --------------------------------------------------------------------------------