xseeks

steel_reserve.py

Aug 23rd, 2012
31
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.24 KB | None | 0 0
  1. #!/usr/bin/env python
  2. ################################################################################
  3. # Program: steel_reserve.py
  4. # Version: 0.8
  5. # Original Author: James Hendrie <hendrie dot james at gmail dot com>
  6. # Contributors:
  7. # License: GPL 3 (http://www.gnu.org/licenses/gpl.html)
  8. # Last edit: 2012-08-23
  9. # Description: A thing I made for spoony. First it renames all the
  10. # files in the current directory to be properly 'unixy',
  11. # then it creates an xml file for each one.
  12. ################################################################################
  13.  
  14. import sys, os
  15. import shutil
  16.  
  17. ## 'constants'
  18. SELF_NAME = sys.argv[0] ## Just for the sake of clarity
  19.  
  20. ## A tuple of things that will be removed from any filename strings
  21. VERBOTEN = ('!',',',':','+','=','&')
  22. LESS_VERBOTEN = ('(',')', ';')
  23.  
  24. ## A tuple of file formats that will be renamed. Everything else is ignored.
  25. ## Do include the period. Don't worry about capitalization.
  26. VALID = ('.mp4','.webm','.flv','.mkv','.m4v')
  27.  
  28. ## When entering tags, convert spaces to commas
  29. ## If false, they're converted to underscores instead
  30. SPACES_TO_COMMAS = True
  31.  
  32. ## Use underscores instead of spaces in tags. If false, the tag will be
  33. ## enclosed in quotation marks. I don't know if that's 'legal' or not, though.
  34. USE_UNDERSCORES = True
  35.  
  36. ## The file activation date
  37. ACTIVATION_DATE = "2012-09-01"
  38.  
  39. REPORT = "[email protected]"
  40.  
  41.  
  42. ## The extension for the xml file
  43. XML_EXTENSION = '.xml'
  44.  
  45. ## Redundant, but I'll keep it here just in case.
  46. XML_TEMPLATE = """
  47. <?xml version="1.0" encoding="UTF-8"?>
  48. <file_upload>
  49. <report>[email protected]</report>
  50. <item>
  51. <filename>ENTER_VIDEO_FILENAME.flv</filename>
  52. <filesize>2901436</filesize>
  53. <title>
  54. <![CDATA[ENTER TITLE HERE]]>
  55. </title>
  56. <duration>21</duration>
  57. <tags>tag1,tag2,tag3</tags>
  58. <age>0</age>
  59. <description>
  60. <![CDATA[ENTER DESCRIPTION HERE]]>
  61. </description>
  62. <activation_date>2012-09-01</activation_date>
  63. </item>
  64. <item>
  65. <filename>ENTER_VIDEO_FILENAME.mp4</filename>
  66. <filesize>3112933</filesize>
  67. <title>
  68. <![CDATA[ENTER TITLE HERE]]>
  69. </title>
  70. <duration>44</duration>
  71. <tags>tag1,tag2,tag3</tags>
  72. <age>0</age>
  73. <description>
  74. <![CDATA[ENTER DESCRIPTION HERE]]>
  75. </description>
  76. <activation_date>2012-09-01</activation_date>
  77. </item>
  78. </file_upload>
  79. """
  80.  
  81. def get_input(queryString):
  82. '''
  83. This exists for compatibility reasons (python 2 vs python 3)
  84. '''
  85. if sys.version[0] == '2':
  86. theInput = raw_input(queryString)
  87. elif sys.version[0] == '3':
  88. theInput = input(queryString)
  89.  
  90. return theInput
  91.  
  92. def set_options():
  93. '''
  94. Set the default options for the program.
  95. '''
  96. ## Defaults
  97. options = {}
  98. options['autogen'] = False
  99. options['activationDate'] = ACTIVATION_DATE
  100.  
  101. options['verbose'] = False ## print what you're removing line by line
  102. options['help'] = False ## Print help
  103. options['version'] = False ## Print version
  104.  
  105. return options
  106.  
  107.  
  108. def dup_finder(aString):
  109. '''
  110. This strips the string of its extension, then looks for a number.
  111. '''
  112. ## The duplicate counter.
  113. dupCount = 0
  114.  
  115. ## Remove all characters up to and including the first period
  116. ## Remember that this is supposed to be a reversed filename string,
  117. ## so the extension comes first ('vlf.oediv_a')
  118. aString = aString.replace(aString[0:aString.index('.')+1],'',1)
  119.  
  120. for i in aString:
  121. try:
  122. dupCount = int(i)
  123. dupCount += 1
  124. break
  125. except ValueError:
  126. pass
  127.  
  128. return dupCount
  129.  
  130.  
  131.  
  132. def string_fixer(stringToBeFixed):
  133. '''
  134. Fixes a string with weird characters
  135. '''
  136. ## Convert the entire string to lowercase
  137. newStr = stringToBeFixed.lower()
  138.  
  139. ## Rename "d&d" to 'dnd'
  140. newStr = newStr.replace('d&d','dnd',-1)
  141.  
  142. ## Remove any verboten characters
  143. for v in VERBOTEN:
  144. newStr = newStr.replace(v, '', -1)
  145.  
  146. for l in LESS_VERBOTEN:
  147. newStr = newStr.replace(l, '_', -1)
  148.  
  149. ## Now that all the verboten characters have been removed, you can
  150. ## optionally replace 'dnd' with 'd&d' again. By default, I assume
  151. ## this isn't wanted. If you do want to do this, uncomment the following
  152. ## line:
  153. #newStr = newStr.replace('dnd', 'd&d', -1)
  154.  
  155. ## Strip leading and trailing whitespace
  156. newStr = newStr.strip()
  157.  
  158. ## Replaces spaces with underscores
  159. newStr = newStr.replace(' ','_',-1)
  160.  
  161. return newStr
  162.  
  163. def tag_fixer(tagToBeFixed):
  164. '''
  165. Similar to string_fixer, except especially for tags
  166. '''
  167. newTag = tagToBeFixed
  168.  
  169. ## Replace some punctuation with what the guy on the forum said
  170. newTag = newTag.replace('&', '&amp;', -1) ## It's important that this be 1st
  171. newTag = newTag.replace('"','&quot;', -1)
  172. newTag = newTag.replace("'",'&apos;', -1)
  173. newTag = newTag.replace('<', '&lt;', -1)
  174. newTag = newTag.replace('>', '&gt;', -1)
  175.  
  176. for v in VERBOTEN:
  177. if v == '&' or v == ';':
  178. pass
  179. else:
  180. newTag = newTag.replace(v, '', -1)
  181.  
  182. ## Strip leading and trailing whitespace
  183. newTag = newTag.strip()
  184.  
  185. if SPACES_TO_COMMAS == True:
  186. newTag = newTag.replace(' ',',',-1)
  187. else:
  188. ## If USE_UNDERSCORES == True, do so. Otherwise, enclose w/quotes
  189. if USE_UNDERSCORES == True:
  190. newTag = newTag.replace(' ', '_', -1)
  191. else:
  192. newTag = '"%s"' % newTag
  193.  
  194. ## Return the 'finished' tag
  195. return newTag
  196.  
  197. def renamer(origName, fileNameListCheck):
  198. '''
  199. Renames the files in the list
  200.  
  201. WARNING: This function is somewhat broken currently. Make sure that you
  202. don't have any files with identical names, or names that WILL be identical
  203. after their names have been 'corrected'
  204. '''
  205.  
  206. newName = string_fixer(origName)
  207.  
  208. ## We should be good by this point, but it's safest to check that the
  209. ## current filename we're working on isn't a duplicate. If it is, number
  210. ## it.
  211.  
  212. ## To ensure the list is 'cleared' of duplicates. We assume that there
  213. ## are, to start with.
  214. doneChecking = 1
  215.  
  216. while (doneChecking > 0):
  217. ## This will change if duplicates are found
  218. doneChecking = 0
  219.  
  220. for n in fileNameListCheck:
  221. if newName == n:
  222. ## Create a reversed copy of newName, so that we can get the
  223. ## last period before the filename extension. There might be a
  224. ## better way to do this, but .... meh.
  225. revNewName = newName[::-1]
  226.  
  227. ## This finds the current 'count' of the possible duplicate
  228. dupCount = dup_finder(revNewName)
  229.  
  230.  
  231. # if dupCount == 0:
  232. # ## Put '2' right after (before) the suffix
  233. # revNewName = revNewName.replace('.','.2_',1)
  234. # else:
  235. # ## Put the duplication interation right after (before)
  236. # ## the extension
  237. # revNewName =revNewName.replace('.','.%s_',1) %str(dupCount)
  238.  
  239. newName = revNewName[::-1]
  240.  
  241. fileNameListCheck.remove(n)
  242.  
  243. ## Increment doneChecking
  244. doneChecking += 1
  245.  
  246. ## Finally, return newName
  247. return newName
  248.  
  249.  
  250. def rename_files():
  251. '''
  252. This function will rename all of the files. Go figure. This is not a very
  253. 'smart' program, so (thus far) it will only work with files in the present
  254. working directory.
  255. '''
  256. ## The lists into which we'll put all of our 'corrected' filenames
  257. fileNameListCheck = []
  258. fileNameList = []
  259.  
  260. ## A list of all the files in the current working directory
  261. listOfFiles = os.listdir(os.getcwd())
  262.  
  263. ## Removes anything that isn't in the list of valid files to be renamed
  264. ## (in other words, anything that isn't a video file)
  265. lof = []
  266. for i in listOfFiles:
  267. for v in VALID:
  268. if v in i:
  269. lof.append(i)
  270.  
  271. listOfFiles = lof
  272.  
  273. ## Create the first list of filenames to check against
  274. for f in lof:
  275. newName = string_fixer(f)
  276. fileNameListCheck.append(newName)
  277.  
  278. ## Now that we've got our first list, we go through the PWD list again
  279. ## checking against that first list for duplicates
  280. for f in listOfFiles:
  281. newName = renamer(f, fileNameListCheck)
  282. fileNameList.append(newName)
  283.  
  284. ## Here, we ensure that the lists are the same length. If they are not,
  285. ## something went terribly wrong and the program needs to exit before
  286. ## any damage is done.
  287. if len(listOfFiles) == len(fileNameList):
  288. pass
  289. else:
  290. print("Error: Mismatched filename lists. Quitting.")
  291. sys.exit()
  292.  
  293. ## And now we return the necessary lists
  294. return listOfFiles, fileNameList
  295.  
  296.  
  297. def move_files(listOfFiles, fileNameList, options):
  298. '''
  299. This does the actual moving (renaming) of the files on the operating system
  300. '''
  301.  
  302. for f in listOfFiles:
  303. if options['verbose'] == True:
  304. print("%s ---> %s" % (f, fileNameList[listOfFiles.index(f)]))
  305. shutil.move(f, fileNameList[listOfFiles.index(f)])
  306.  
  307.  
  308. def generate_xml(fileData):
  309. '''
  310. This function does the actual XML generation
  311. '''
  312. xml = """
  313. <?xml version="1.0" encoding="UTF-8"?>
  314. <file_upload>
  315. <report>%s</report>
  316. <item>
  317. <filename>%s</filename>
  318. <title>
  319. <![CDATA[%s]]>
  320. </title>
  321. <duration>%s</duration>
  322. <tags>%s</tags>
  323. <age>%s</age>
  324. <description>
  325. <![CDATA[%s]]>
  326. </description>
  327. <activation_date>%s</activation_date>
  328. </item>
  329. <item>
  330. <filename>%s</filename>
  331. <title>
  332. <![CDATA[%s]]>
  333. </title>
  334. <duration>%s</duration>
  335. <tags>%s</tags>
  336. <age>%s</age>
  337. <description>
  338. <![CDATA[%s]]>
  339. </description>
  340. <activation_date>%s</activation_date>
  341. </item>
  342. </file_upload>
  343. """ % (REPORT,
  344. fileData['name'],
  345. fileData['title'],
  346. fileData['duration'],
  347. fileData['tags'],
  348. fileData['ageCap'],
  349. fileData['description'],
  350. fileData['activationDate'],
  351. fileData['name'],
  352. fileData['title'],
  353. fileData['duration'],
  354. fileData['tags'],
  355. fileData['ageCap'],
  356. fileData['description'],
  357. fileData['activationDate'])
  358.  
  359. return xml
  360.  
  361. def generate_tag_list(aStr):
  362. '''
  363. This takes a string and creates a list of tags, with a new tag happening
  364. after every comma
  365. '''
  366. tagList = []
  367.  
  368. ## Grab each tag from the string
  369. while(len(aStr) > 0):
  370. try:
  371. tag = aStr[0:aStr.index(',')]
  372. aStr = aStr.replace(aStr[0:aStr.index(',')+1],'',1)
  373. tagList.append(tag)
  374. except ValueError:
  375. tag = aStr[0:-1]
  376. tagList.append(tag)
  377. aStr = ""
  378.  
  379. return tagList
  380.  
  381.  
  382. def get_description(fileName, options):
  383. '''
  384. Have the user input a basic description for the file.
  385. '''
  386. descStr = """
  387. Now you'll enter a description for the file. It will look ugly as you type,
  388. but that's unavoidable (at least, for the effort I'm willing to put in).
  389. Just type whatever and hit enter when you're done.
  390. \n
  391. Note: To use the previously used description, leave this field blank and
  392. hit enter. To use no description, just put a zero (0) and hit enter.
  393. """
  394.  
  395. print(descStr)
  396. print("Filename: %s" % fileName)
  397.  
  398. queryString = "Description: "
  399.  
  400. description = get_input(queryString)
  401.  
  402. if description == "":
  403. description = options['prevDescription']
  404. elif description == '0':
  405. description = ""
  406.  
  407. return description
  408.  
  409.  
  410. def get_tags(fileName):
  411. '''
  412. We let the user enter their own tags manually.
  413. '''
  414. ## Empty 'tags' string
  415. tags = ""
  416.  
  417. print('\n\n\n---------------------------', fileName)
  418. print("\nEnter tags as a comma-separated list:\n")
  419.  
  420. if SPACES_TO_COMMAS == True:
  421. print("(Spaces will be converted to commas)\n")
  422. else:
  423. if USE_UNDERSCORES == True:
  424. print("(Spaces will be converted to underscores)\n")
  425. else:
  426. print("(Your tags will be enclosed in quotation marks)\n")
  427.  
  428. ## The query string is what the user sees for a prompt
  429. queryString = "tags: "
  430.  
  431. ## Get the input (in this case, comma-separated list of tags)
  432. print("Filename: ", fileName)
  433. theInput = get_input(queryString)
  434.  
  435. ## Create a tag list according to input
  436. tagList = generate_tag_list(theInput)
  437.  
  438. for tag in tagList:
  439. tag = tag_fixer(tag)
  440. ## Add a comma and the fixed tag to the tag string
  441. tags += ",%s" % tag
  442.  
  443. ## Finally, return the tags string
  444. return tags
  445.  
  446. def autogen_tags(fileName):
  447. '''
  448. Automatically generate some tags
  449. '''
  450. ## As mentioned, this isn't ideal but... whatever.
  451. tagString = fileName
  452.  
  453. for v in VALID:
  454. if v in tagString:
  455. tagString = tagString.replace(v, '', -1)
  456. elif v.upper() in tagString:
  457. tagString = tagString.replace(v.upper(), '', -1)
  458.  
  459. tagString = tagString.replace('_',',',-1)
  460. tagString = tagString.replace('-',',',-1)
  461.  
  462. return tagString
  463.  
  464.  
  465. def xml_the_files(fileNameList, options):
  466. '''
  467. This function generates some (less than ideal) metadata for each file
  468. in the list of files passed to it. Then it creates an XML file for each
  469. one, named identically to the file except with a .xml extension.
  470. '''
  471. for f in fileNameList:
  472. fileData = {}
  473.  
  474. ## Used later
  475. ft = ""
  476.  
  477. ## Set file name
  478. fileData['name'] = f
  479.  
  480. ## Set file size. Disabled by default
  481. #fileData['fileSize'] = os.stat(f)[6]
  482.  
  483. ## Autogenerate some tags
  484. tags = autogen_tags(f)
  485. fileData['tags'] = tags
  486.  
  487. ## If the user does not want to use any autogenerated tags
  488. if options['autogen'] == False:
  489. tags = get_tags(f)
  490. fileData['tags'] = tags
  491.  
  492. ## Here we remove the extension from the filename
  493. for v in VALID:
  494. if v in f:
  495. ft = f.replace(v, '', -1)
  496.  
  497. ## Create a fancy title for the CDATA block. God I love Python.
  498. fancyTitle = ft.title()
  499. fancyTitle = fancyTitle.replace('_',' ', -1)
  500.  
  501. ## Title of the file
  502. fileData['title'] = fancyTitle
  503.  
  504. ## Duration of the video. '-||-' is used in place of actual duration
  505. fileData['duration'] = "-||-"
  506.  
  507. ## The age cap
  508. fileData['ageCap'] = "0"
  509.  
  510. ## If autogen is on, set the description to be the same as the file's
  511. ## title. Otherwise, have the user manually enter the description
  512. if options['autogen'] == True:
  513. fileData['description'] = ft
  514. else:
  515. fileData['description'] = get_description(f, options)
  516. options['prevDescription'] = fileData['description']
  517.  
  518. ## Set the file activation date
  519. fileData['activationDate'] = options['activationDate']
  520.  
  521. ## Finally, we generate the actual XML
  522. xml = generate_xml(fileData)
  523.  
  524. ## We get rid of the top newline, just to make it prettier
  525. xml = xml.replace('\n','',1)
  526.  
  527. ## Create a name for the new XML file
  528. xmlName = "%s%s" % (ft, XML_EXTENSION)
  529.  
  530. ## And finally, write it to disk
  531. try:
  532. xmlFile = open(xmlName, 'w')
  533. for line in xml:
  534. xmlFile.write(line)
  535. if options["verbose"] == True:
  536. print("Generating %s" % xmlName)
  537. xmlFile.close()
  538. except IOError:
  539. print("""
  540. Error: Cannot open %s for writing. Do you have permissions?
  541. """ % xmlName)
  542. sys.exit()
  543.  
  544. def get_options(options):
  545. '''
  546. Get options from the command line
  547. '''
  548.  
  549. for i in sys.argv:
  550. if i == '-v':
  551. options['verbose'] = True
  552. elif i == '--verbose':
  553. options['verbose'] = True
  554. elif i == '-h':
  555. options['help'] = True
  556. elif i == '--help':
  557. options['help'] = True
  558. elif i == '--version':
  559. options['version'] = 1
  560. elif i == '--auto':
  561. options['autogen'] = True
  562.  
  563. return options
  564.  
  565.  
  566. def print_help():
  567. '''
  568. Print the help
  569. '''
  570. helpStr = """
  571. This script will look through a directory of files (video files, by default)
  572. and generate an XML file for each one. Each XML file will have some basic
  573. metadata, which can be automatically generated if you don't mind having
  574. shitty tags.
  575.  
  576. Currently, this script must be run inside the directory where all of the
  577. files to be read/generated are.
  578.  
  579. usage: steel_reserve.py [argument]
  580.  
  581. Arguments:
  582.  
  583. -h or --help: Print help
  584. -v or --verbose: Verbosity
  585. --version: Print version / info
  586. --auto: Use autogeneration (easiest)
  587.  
  588. """
  589. print(helpStr)
  590.  
  591.  
  592. def print_version():
  593. '''
  594. Prints the version stuff
  595. '''
  596. versionStr = """
  597.  
  598. Program: steel_reserve.py
  599. Version: 0.8
  600. License: GPL 3 (http://www.gnu.org/licenses/gpl.html)
  601. Last edit: 2012-08-23
  602.  
  603. Description: Generates XML files for files (video files by default) in
  604. the current working directory.
  605.  
  606. """
  607. print(versionStr)
  608.  
  609. def main():
  610. '''
  611. The main function. First we rename all the files as necessary, then we get
  612. the necessary info about them, then we create the XML files for them.
  613. '''
  614. ## Get the options for the program
  615. options = set_options()
  616.  
  617. ## If options were passed on the command line, set them here
  618. options = get_options(options)
  619.  
  620. ## Do the help/version stuff
  621. if options['help'] == True:
  622. print_help()
  623. sys.exit()
  624. if options['version'] == True:
  625. print_version()
  626. sys.exit()
  627.  
  628. ## Get the lists of original filenames and their replacements
  629. listOfFiles, fileNameList = rename_files()
  630.  
  631. ## Move (rename) the files
  632. if options['verbose'] == True:
  633. print("\n================= RENAMING FILES ================\n")
  634. move_files(listOfFiles, fileNameList, options)
  635.  
  636. ## Generate metadata for each file, then create an XML file for it
  637. if options["verbose"] == True:
  638. print("\n=============== GENERATING XML FILES ================\n")
  639. xml_the_files(fileNameList, options)
  640.  
  641. ## One final line to separate output from the prompt
  642. if options['verbose'] == True:
  643. print("")
  644.  
  645. if __name__ == "__main__":
  646. main()
Advertisement
Add Comment
Please, Sign In to add comment