├── LICENSE
├── README.md
├── itunesheader.txt
├── itunesitems.txt
└── podcast_live_template.py
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Gordon Haff
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # podcast-python
2 | Script for automating the uploading of a new podcast
3 |
4 | This script:
5 |
6 | 1. Gets information such as duration from MP3 file
7 | 2. Allows user to input additional information (title, etc.)
8 | 3. Updates iTunes XML podcast file
9 | 4. Concatenates MP3 file with intro and outro segments
10 | 5. Creates OGG file version
11 | 6. Uploads XML and MP3 and OGG files to Amazon S3 and makes PUBLIC
12 |
13 | Requires:
14 |
15 | - boto https://github.com/boto/boto.git
16 | - mpeg1audio https://github.com/Ciantic/mpeg1audio/
17 | - pydub (which requires ffmpeg to be installed) https://github.com/jiaaro/pydub
18 | - Edited MP3 file
19 | - MP3 intro and outro segments
20 | - Image file public on S3
21 | - header XML in header txt file
22 | - text file to store individual podcast XML, even if initially (mostly) empty.
23 | - Needs lines:
24 | -
25 | -
26 | - An existing RSS podcast feed XML file (or a null file)
27 | - Existing AWS S3 bucket and credentials
28 | - Supporting files in same directory as MP3 file
29 | - set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY as environment variables
30 | - Redefine the global filename variables for your needs
31 | - Redefine the bucket name for your needs
32 |
33 |
34 |
--------------------------------------------------------------------------------
/itunesheader.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | YOUR PODCAST TITLE
7 | http://www.MYWEBSITE.com
8 | en-us
9 | YOUR COPYRIGHT INFO
10 | THE LIFE AND TIMES OF DUNG BEETLES
11 | YOUR NAME
12 | A LONGER SUMMARY THAT DESCRIBES YOUR PODCAST
13 | Industry experts talk cloud computing, DevOps, IoT, containers, and more.
14 |
15 | YOUR NAME
16 | YOUR EMAIL
17 |
18 |
19 |
20 | no
21 |
22 |
--------------------------------------------------------------------------------
/itunesitems.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/podcast_live_template.py:
--------------------------------------------------------------------------------
1 | ####################
2 | # Podcast post-production
3 | #!/usr/bin/python
4 |
5 | # Gordon Haff
6 | #
7 | # Requires:
8 | # boto https://github.com/boto/boto.git
9 | # mpeg1audio https://github.com/Ciantic/mpeg1audio/
10 | # pydub (which requires ffmpeg to be installed) https://github.com/jiaaro/pydub
11 | # Edited MP3 file
12 | # MP3 intro and outro segments
13 | # Image file public on S3
14 | # header XML in header txt file
15 | # text file to store individual podcast XML, even if initially (mostly) empty.
16 | # Needs lines:
17 | #
18 | #
19 | # An existing RSS podcast feed XML file (or a null file)
20 | # Existing AWS S3 bucket and credentials
21 | # Supporting files in same directory as MP3 file
22 | #
23 | # set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY as environment variables
24 | # Redefine the global filename variables for your needs
25 | # Redefine the bucket name for your needs
26 | #
27 | # This script:
28 | # 1. Gets information such as duration from MP3 file
29 | # 2. Allows user to input additional information (title, etc.)
30 | # 3. Updates iTunes XML podcast file
31 | # 4. Concatenates MP3 file with intro and outro segments
32 | # 5. Creates OGG file version
33 | # 6. Uploads XML and MP3 and OGG files to Amazon S3 and makes PUBLIC
34 | #
35 |
36 | from Tkinter import *
37 | from pydub import AudioSegment
38 | import tkFileDialog
39 | import os
40 | import boto
41 | from boto.s3.key import Key
42 | from os import path
43 | import time
44 | import mpeg1audio
45 | from gdata import service
46 | import gdata
47 | import atom
48 | import shutil
49 |
50 | #####################################################
51 | # Define this stuff
52 | # set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY as environment variables
53 | bucket_name='MYPODCASTBUCKET'
54 | ##############################################
55 |
56 | def open_file_dialog():
57 |
58 | global filename
59 | global oggexists
60 | global Filelength
61 | global FilelengthStr
62 | global DurationStr
63 | global OggFilename
64 | global iTunesFile
65 | global iTunesHeader
66 | global iTunesItems
67 | global theDirname
68 |
69 | # Change names as desired
70 | leadIn = "podcastintro.mp3" #intro to prepend
71 | leadOut = "podcastoutro.mp3" #outro to append
72 | iTunesFile = "itunesrss.xml" #your podcast feed
73 | iTunesHeader = "itunesheader.txt" #the header on your podcast feed file
74 | iTunesItems = "itunesitems.txt" #you'll need to populate with XML for a podcast
75 |
76 | filename = tkFileDialog.askopenfilename(filetypes=[("MP3 files",".mp3")])
77 | v.set(filename)
78 |
79 | FileBase, FileExtension = path.splitext(filename)
80 | theDirname = path.dirname(filename)
81 |
82 | renameOriginal = FileBase + "_original" + FileExtension
83 | shutil.copy2(filename,renameOriginal)
84 |
85 | # add path to the various locations
86 | leadIn = path.join(theDirname,leadIn)
87 | leadOut = path.join(theDirname,leadOut)
88 | iTunesFile = path.join(theDirname,iTunesFile)
89 | iTunesHeader = path.join(theDirname,iTunesHeader)
90 | iTunesItems = path.join(theDirname,iTunesItems)
91 |
92 | # Concatenate MP3 file with header and footer
93 | baseSegment = AudioSegment.from_mp3(filename)
94 | introSegment = AudioSegment.from_mp3(leadIn)
95 | outroSegment = AudioSegment.from_mp3(leadOut)
96 |
97 | completeSegment = introSegment + baseSegment + outroSegment
98 |
99 | #export new MP3 file and also an ogg version
100 | completeSegment.export(filename,"mp3")
101 | OggFilename = FileBase + '.ogg'
102 | completeSegment.export(OggFilename,"ogg")
103 |
104 | # Error checking from prior version. "Shouldn't" be possible to happen now
105 | oggexists = True
106 | if not path.isfile(OggFilename):
107 | oggexists = False
108 | StatusText.set("Status: OGG file missing")
109 |
110 | Filelength = path.getsize(filename)
111 | FilelengthStr.set("Filelength (bytes): " + str(Filelength))
112 |
113 | timestruc = time.gmtime(path.getmtime(filename))
114 |
115 | TimestampEntry.delete(0,END)
116 | TimestampEntry.insert(0,time.strftime("%a, %d %b %G %T",timestruc) + " GMT")
117 |
118 | mp3 = mpeg1audio.MPEGAudio(filename)
119 | DurationStr = str(mp3.duration)
120 | DurationLabelStr.set("Duration: " + DurationStr)
121 |
122 | def do_stuff():
123 |
124 | createXML()
125 | uploadtoAMZN()
126 | StatusText.set("Status: Success (AFAIK)")
127 |
128 | def createXML():
129 |
130 | global MP3url
131 |
132 | # create an XML file containing contents for new for iTunes
133 | FileBase, FileExtension = path.splitext(filename)
134 | XMLfilename = FileBase + '.xml'
135 | MP3url = "http://s3.amazonaws.com/"+bucket_name+"/"+path.basename(filename)
136 | inp = file(XMLfilename, 'w')
137 |
138 | inp.write("- \n")
139 | inp.write(""+PodcastTitleEntry.get()+"\n")
140 | inp.write(""+PodcastSubtitleEntry.get()+"\n")
141 | inp.write(""+PodcastSummaryText.get(1.0,END)+"\n")
142 | inp.write("\n")
143 | inp.write(""+MP3url+"\n")
144 | inp.write(""+TimestampEntry.get()+"\n")
145 | inp.write(""+DurationStr+"\n")
146 | inp.write("cloud\n")
147 | inp.write("no\n")
148 | inp.write("
")
149 | inp.write("")
150 |
151 | inp.close()
152 |
153 | #Now concatenate to make a new itunesxml.xml file
154 |
155 | #create backup of existing iTunes XML file in case something goes kaka
156 | iTunesBackup = path.join(theDirname,"itunesxmlbackup.xml")
157 | shutil.copy2(iTunesFile,iTunesBackup)
158 |
159 | #create temporary iTunes item list (to overwrite the old one later on)
160 | outfile = file("iTunestemp.xml", 'w')
161 |
162 | # create a new items file
163 | with open(XMLfilename) as f:
164 | for line in f:
165 | outfile.write(line)
166 | with open(iTunesItems) as f:
167 | for line in f:
168 | outfile.write(line)
169 | outfile.close()
170 |
171 | #replace the old items file with the new one
172 | shutil.copy2("iTunestemp.xml",iTunesItems)
173 |
174 | #now we're ready to create the new iTunes File
175 | outfile = file(iTunesFile, 'w')
176 |
177 | # create a new items file
178 | with open(iTunesHeader) as f:
179 | for line in f:
180 | outfile.write(line)
181 | with open(iTunesItems) as f:
182 | for line in f:
183 | outfile.write(line)
184 | outfile.close()
185 |
186 | def uploadtoAMZN():
187 |
188 | # Upload files to Amazon S3
189 | # Change 'public-read' to 'private' if you want to manually set ACLs
190 | conn = boto.connect_s3()
191 | bucket = conn.get_bucket(bucket_name)
192 | k = Key(bucket)
193 | k.key = path.basename(filename)
194 | k.set_contents_from_filename(filename)
195 | k.set_canned_acl('public-read')
196 |
197 | if oggexists:
198 | k.key = path.basename(OggFilename)
199 | k.set_contents_from_filename(OggFilename)
200 | k.set_canned_acl('public-read')
201 |
202 | k.key = path.basename(iTunesFile)
203 | k.set_contents_from_filename(iTunesFile)
204 | k.set_canned_acl('public-read')
205 |
206 | #####################################################
207 |
208 | root = Tk()
209 |
210 | Label(root,text="Podcast Title:").grid(row=1, sticky=W)
211 |
212 | PodcastTitleEntry = Entry(root, width=80, borderwidth=1)
213 | PodcastTitleEntry.grid(row=2, sticky=W)
214 |
215 | Label(root,text="iTunes subtitle:").grid(row=3, sticky=W)
216 |
217 | PodcastSubtitleEntry=Entry(root, width=80, borderwidth=1)
218 | PodcastSubtitleEntry.grid(row=4, sticky=W)
219 |
220 | Label(root,text="iTunes summary:").grid(row=5, sticky=W)
221 |
222 | PodcastSummaryText=Text(root,width=80,height=4,borderwidth=2)
223 | PodcastSummaryText.grid(row=6,sticky=W)
224 |
225 |
226 | Button(root, text='Select file...',command=open_file_dialog).grid(row=9, column=0, sticky=W)
227 |
228 | v = StringVar()
229 | Label(root, textvariable=v,justify=LEFT,fg="blue").grid(row=10,sticky=W)
230 |
231 | TimestampEntry = Entry(root,width=50,borderwidth=1)
232 | TimestampEntry.grid(row=11,sticky=W)
233 | TimestampEntry.insert(END,"Time/date (default filled in automatically from file)")
234 |
235 | FilelengthStr = StringVar()
236 | FilelengthStr.set("Filelength (bytes):")
237 |
238 | FilelengthLabel = Label(root,textvariable=FilelengthStr)
239 | FilelengthLabel.grid(row=12,sticky=W)
240 |
241 | DurationLabelStr = StringVar()
242 | DurationLabelStr.set("Duration: ");
243 | DurationLabel = Label(root,textvariable=DurationLabelStr)
244 | DurationLabel.grid(row=13,sticky=W)
245 |
246 | Button(root, text='Go!',command=do_stuff).grid(row=14, sticky=W)
247 |
248 | StatusText = StringVar()
249 | StatusText.set("Status: Nothing to report")
250 |
251 | StatusLabel=Label(root,textvariable=StatusText)
252 | StatusLabel.grid(row=15, sticky=W)
253 |
254 | root.mainloop()
255 |
256 |
257 |
258 |
--------------------------------------------------------------------------------