Play images and video from Synology PhotoStation server

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. # -*- coding: utf-8 -*-
  2. """
  3. kodiswift.cli.create
  4. ---------------------
  5. This module contains the code to initialize a new Kodi addon project.
  6. :copyright: (c) 2012 by Jonathan Beluch
  7. :license: GPLv3, see LICENSE for more details.
  8. """
  9. from __future__ import print_function, absolute_import
  10. import os
  11. import readline
  12. import string
  13. from getpass import getpass
  14. from optparse import OptionParser
  15. from os import getcwd
  16. from shutil import copytree, ignore_patterns
  17. from xml.sax import saxutils
  18. class CreateCommand(object):
  19. """A CLI command to initialize a new Kodi addon project."""
  20. command = 'create'
  21. usage = '%prog create'
  22. # noinspection PyUnusedLocal
  23. @staticmethod
  24. def run(opts, args):
  25. """Required run function for the 'create' CLI command."""
  26. create_new_project()
  27. # Path to skeleton file templates dir
  28. SKEL = os.path.join(os.path.dirname(__file__), 'data')
  29. def error_msg(msg):
  30. """A decorator that sets the error_message attribute of the decorated
  31. function to the provided value.
  32. """
  33. def decorator(func):
  34. """Sets the error_message attribute on the provided function"""
  35. func.error_message = msg
  36. return func
  37. return decorator
  38. def parse_cli():
  39. """Currently only one positional arg, create."""
  40. parser = OptionParser()
  41. return parser.parse_args()
  42. @error_msg('** Value must be non-blank.')
  43. def validate_nonblank(value):
  44. """A callable that retunrs the value passed"""
  45. return value
  46. @error_msg('** Value must contain only letters or underscores.')
  47. def validate_pluginid(value):
  48. """Returns True if the provided value is a valid plugin id"""
  49. valid = string.ascii_letters + string.digits + '.' + '_'
  50. return all(c in valid for c in value)
  51. @error_msg('** The provided path must be an existing folder.')
  52. def validate_isfolder(value):
  53. """Returns true if the provided path is an existing directory"""
  54. return os.path.isdir(value)
  55. def get_valid_value(prompt, validator, default=None):
  56. """Displays the provided prompt and gets input from the user. This behavior
  57. loops indefinitely until the provided validator returns True for the user
  58. input. If a default value is provided, it will be used only if the user
  59. hits Enter and does not provide a value.
  60. If the validator callable has an error_message attribute, it will be
  61. displayed for an invalid value, otherwise a generic message is used.
  62. """
  63. ans = get_value(prompt, default)
  64. while not validator(ans):
  65. try:
  66. print(validator.error_message)
  67. except AttributeError:
  68. print('Invalid value.')
  69. ans = get_value(prompt, default)
  70. return ans
  71. def get_value(prompt, default=None, hidden=False):
  72. """Displays the provided prompt and returns the input from the user. If the
  73. user hits Enter and there is a default value provided, the default is
  74. returned.
  75. """
  76. _prompt = '%s : ' % prompt
  77. if default:
  78. _prompt = '%s [%s]: ' % (prompt, default)
  79. if hidden:
  80. ans = getpass(_prompt)
  81. else:
  82. ans = raw_input(_prompt)
  83. # If user hit Enter and there is a default value
  84. if not ans and default:
  85. ans = default
  86. return ans
  87. def update_file(filename, items):
  88. """Edits the given file in place, replacing any instances of {key} with the
  89. appropriate value from the provided items dict. If the given filename ends
  90. with ".xml" values will be quoted and escaped for XML.
  91. """
  92. # TODO: Implement something in the templates to denote whether the value
  93. # being replaced is an XML attribute or a value. Perhaps move to dyanmic
  94. # XML tree building rather than string replacement.
  95. should_escape = filename.endswith('addon.xml')
  96. with open(filename, 'r') as inp:
  97. text = inp.read()
  98. for key, val in items.items():
  99. if should_escape:
  100. val = saxutils.quoteattr(val)
  101. text = text.replace('{%s}' % key, val)
  102. output = text
  103. with open(filename, 'w') as out:
  104. out.write(output)
  105. def create_new_project():
  106. """Creates a new Kodi Plugin directory based on user input"""
  107. readline.parse_and_bind('tab: complete')
  108. print("""
  109. kodiswift - A micro-framework for creating Kodi plugins.
  110. xbmc@jonathanbeluch.com
  111. --
  112. """)
  113. print('I\'m going to ask you a few questions to get this project started.')
  114. opts = {}
  115. # Plugin Name
  116. opts['plugin_name'] = get_valid_value(
  117. 'What is your plugin name?',
  118. validate_nonblank
  119. )
  120. # Plugin ID
  121. opts['plugin_id'] = get_valid_value(
  122. 'Enter your plugin id.',
  123. validate_pluginid,
  124. 'plugin.video.%s' % (opts['plugin_name'].lower().replace(' ', ''))
  125. )
  126. # Parent Directory
  127. opts['parent_dir'] = get_valid_value(
  128. 'Enter parent folder (where to create project)',
  129. validate_isfolder,
  130. getcwd()
  131. )
  132. # Parent Directory
  133. opts['plugin_dir'] = os.path.join(opts['parent_dir'], opts['plugin_id'])
  134. assert not os.path.isdir(opts['plugin_dir']), \
  135. 'A folder named %s already exists in %s.' % (opts['plugin_id'],
  136. opts['parent_dir'])
  137. # Provider
  138. opts['provider_name'] = get_valid_value(
  139. 'Enter provider name',
  140. validate_nonblank,
  141. )
  142. # Create the project folder by copying over skel
  143. copytree(SKEL, opts['plugin_dir'], ignore=ignore_patterns('*.pyc'))
  144. # Walk through all the new files and fill in with out options
  145. for root, _, files in os.walk(opts['plugin_dir']):
  146. for filename in files:
  147. update_file(os.path.join(root, filename), opts)
  148. print('Projects successfully created in %s.' % opts['plugin_dir'])
  149. print('Done.')