Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ sickbeard.db*
autoProcessTV/autoProcessTV.cfg
server.crt
server.key
SickBeard-vassal.ini

# SB Test Related #
######################
Expand All @@ -33,6 +34,7 @@ tests/cache.db

# OS generated files #
######################
.idea
.Spotlight-V100
.Trashes
.DS_Store
Expand Down
33 changes: 33 additions & 0 deletions SickBeard-vassal.ini.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[uwsgi]
plugins = python27, gevent27
http_socket = http://127.0.0.1:3003

uid = nginx
gid = nginx

chdir = /opt/Sick-Beard

; Listening interface and port,
env = SICKBEARD_LISTEN=127.0.0.1
env = SICKBEARD_PORT=8081

;Location of your configuration file
env = SICKBEARD_CONF=config.ini

;Force an update on start
;env = SICKBEARD_UPDATE=0

;Auto-resize the header with PIL
;env = SICKBEARD_NO_RESIZE=0

;Data root, defaults to the cwd/data
;env = SICKBEARD_DATA_DIR=""

;Directory to store your logs
;env = SICKBEARD_LOGDIR=""

module = SickBeard_wsgi:app
threads = 1
processes = 1

socket = /tmp/sickbeard.sock
228 changes: 228 additions & 0 deletions SickBeard_wsgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#!/usr/bin/env python
# Author: Nic Wolfe <nic@wolfeden.ca>
# Additions by:
__author__ = 'Rob MacKinnon <rob.mackinnon@gmail.com>'
#__name__ = 'SickBeard_wsgi'
# URL: http://code.google.com/p/sickbeard/
#
# Sick Beard 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 Sick Beard. If not, see <http://www.gnu.org/licenses/>.

import sys
if sys.version_info < (2, 5):
sys.exit("Sorry, requires Python 2.5, 2.6 or 2.7.")

try:
import Cheetah
if Cheetah.Version[0] != '2':
raise ValueError
except ValueError:
sys.exit("Sorry, requires Python module Cheetah 2.1.0 or newer.")
except:
sys.exit("The Python module Cheetah is required")

# We only need this for compiling an EXE and I will just always do that on 2.6+
if sys.hexversion >= 0x020600F0:
from multiprocessing import freeze_support # @UnresolvedImport


import locale
import os
import threading
import signal
import time

import sickbeard
from sickbeard import logger
from sickbeard.version import SICKBEARD_VERSION

from sickbeard.webserveInit import (
app as CHERRYPY_APP,
initWebServer
)


from lib.configobj import ConfigObj

from SickBeard import daemonize, loadShowsFromDB


def getEnvironmentFlag(flag, default):
try:
if os.environ[flag] is not None:
if os.environ[flag] == "1":
return True
else:
return False
except:
return default


def getEnvironmentStr(flag):
try:
if os.environ[flag] is not None:
return os.environ[flag]
except:
return None


def main():
"""
TV for me
"""

# do some preliminary stuff
sickbeard.MY_FULLNAME = os.path.normpath(os.path.abspath(__file__))
sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME)
sickbeard.PROG_DIR = os.path.dirname(sickbeard.MY_FULLNAME)
sickbeard.DATA_DIR = sickbeard.PROG_DIR
sickbeard.MY_ARGS = sys.argv[1:]
sickbeard.DAEMON = False
sickbeard.CREATEPID = False

sickbeard.SYS_ENCODING = None

try:
locale.setlocale(locale.LC_ALL, "")
sickbeard.SYS_ENCODING = locale.getpreferredencoding()
except (locale.Error, IOError):
pass

# For OSes that are poorly configured I'll just randomly force UTF-8
if not sickbeard.SYS_ENCODING or sickbeard.SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
sickbeard.SYS_ENCODING = 'UTF-8'

if not hasattr(sys, "setdefaultencoding"):
reload(sys)

try:
# pylint: disable=E1101
# On non-unicode builds this will raise an AttributeError, if encoding type is not valid it throws a LookupError
sys.setdefaultencoding(sickbeard.SYS_ENCODING)
except:
sys.exit("Sorry, you MUST add the Sick Beard folder to the PYTHONPATH environment variable\n" +
"or find another way to force Python to use " + sickbeard.SYS_ENCODING + " for string encoding.")

# Need console logging for SickBeard.py and SickBeard-console.exe
consoleLogging = (not hasattr(sys, "frozen")) or (sickbeard.MY_NAME.lower().find('-console') > 0)

# Rename the main thread
threading.currentThread().name = "MAIN"

forceUpdate = getEnvironmentFlag('SICKBEARD_UPDATE', False)
forcedPort = getEnvironmentStr('SICKBEARD_PORT')
forcedIP = getEnvironmentStr('SICKBEARD_LISTEN')
noLaunch = True # The WSGI version should never try to load a browser

sickbeard.DAEMON = False # Don't ever fork
consoleLogging = getEnvironmentFlag('SICKBEARD_CONSOLELOG', True)
sickbeard.CREATEPID = False
sickbeard.NO_RESIZE = getEnvironmentFlag('SICKBEARD_NO_RESIZE', False)

if getEnvironmentStr('SICKBEARD_DATA_DIR') is not None:
sickbeard.DATA_DIR = os.path.abspath(getEnvironmentStr('SICKBEARD_DATA_DIR'))

if getEnvironmentStr('SICKBEARD_CONF') is not None:
sickbeard.CONFIG_FILE = getEnvironmentStr('SICKBEARD_CONF')
else:
sickbeard.CONFIG_FILE = os.path.join(sickbeard.DATA_DIR, "config.ini")

if getEnvironmentStr('SICKBEARD_LOGDIR') is not None:
sickbeard.DATA_DIR = os.path.abspath(getEnvironmentStr('SICKBEARD_LOGDIR'))

if not os.access(sickbeard.CONFIG_FILE, os.W_OK):
if os.path.isfile(sickbeard.CONFIG_FILE):
sys.exit("Config file: " + sickbeard.CONFIG_FILE + " must be writeable (write permissions). Exiting.")
elif not os.access(os.path.dirname(sickbeard.CONFIG_FILE), os.W_OK):
sys.exit("Config file directory: " + os.path.dirname(sickbeard.CONFIG_FILE) + " must be writeable (write permissions). Exiting")

os.chdir(sickbeard.DATA_DIR)

if consoleLogging:
sys.stdout.write("Starting up Sick Beard " + SICKBEARD_VERSION + "\n")
if not os.path.isfile(sickbeard.CONFIG_FILE):
sys.stdout.write("Unable to find '" + sickbeard.CONFIG_FILE + "' , all settings will be default!" + "\n")

# Load the config and publish it to the sickbeard package
sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)

sickbeard.VERSION_NOTIFY = getEnvironmentFlag('SICKBEARD_VERSION_UPDATE', False)

# Initialize the config and our threads
sickbeard.initialize(consoleLogging=consoleLogging)

sickbeard.showList = []

if sickbeard.DAEMON:
daemonize()

# Use this PID for everything
sickbeard.PID = os.getpid()

if forcedPort:
logger.log(u"Forcing web server to port " + str(forcedPort))
startPort = int(forcedPort)
else:
startPort = sickbeard.WEB_PORT

if sickbeard.WEB_LOG:
log_dir = sickbeard.LOG_DIR
else:
log_dir = None

if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
webhost = sickbeard.WEB_HOST
else:
if sickbeard.WEB_IPV6:
webhost = '::'
else:
webhost = '0.0.0.0'

if forcedIP is not None:
webhost = forcedIP

try:
initWebServer({
'port': startPort,
'host': webhost,
'data_root': os.path.join(sickbeard.PROG_DIR, 'data'),
'web_root': sickbeard.WEB_ROOT,
'log_dir': log_dir,
'username': sickbeard.WEB_USERNAME,
'password': sickbeard.WEB_PASSWORD,
'enable_https': sickbeard.ENABLE_HTTPS,
'https_cert': sickbeard.HTTPS_CERT,
'https_key': sickbeard.HTTPS_KEY,
})
except IOError:
logger.log(u"Unable to start web server, is something else running on port: " + str(startPort), logger.ERROR)
if sickbeard.LAUNCH_BROWSER and not sickbeard.DAEMON:
logger.log(u"Launching browser and exiting", logger.ERROR)
sickbeard.launchBrowser(startPort)
sys.exit("Unable to start web server, is something else running on port: " + str(startPort))

# Build from the DB to start with
logger.log(u"Loading initial show list")
loadShowsFromDB()

# Fire up all our threads
sickbeard.start()

# Start an update if we're supposed to
if forceUpdate:
sickbeard.showUpdateScheduler.action.run(force=True) # @UndefinedVariable

return CHERRYPY_APP

signal.signal(signal.SIGINT, sickbeard.sig_handler)
signal.signal(signal.SIGHUP, sickbeard.sig_handler)
signal.signal(signal.SIGQUIT, sickbeard.sig_handler)
signal.signal(signal.SIGTERM, sickbeard.sig_handler)

print "Starting WSGI application"
app = main()
6 changes: 6 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ Sick Beard makes use of the following projects:
To run Sick Beard from source you will need Python 2.5+ and Cheetah 2.1.0+.
The [binary releases][githubdownloads] are standalone. Build 503 and older can be found on the now defunct [legacy releases][googledownloads].

## Additional Features

### Run As Managed WSGI Application
As of this fork, the cherrypy.server.wait() and invoked command loops have been removed for efficiency. Service starts and is minded by uWSGI Emperor if you have it.
To setup, update SickBeard-vassal.ini.example with your working directory and save to the vassal INI to your uWSGI Emperor directory.

## Bugs

If you find a bug please report it or it'll never get fixed. Verify that it hasn't [already been submitted][googleissues] and then [log a new bug][googlenewissue]. Be sure to provide as much information as possible.
Expand Down
4 changes: 2 additions & 2 deletions sickbeard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -953,8 +953,8 @@ def saveAndShutdown(restart=False):
logger.log(u"Restarting Sick Beard with " + str(popen_list))
logger.close()
subprocess.Popen(popen_list, cwd=os.getcwd())

os._exit(0)
sys.exit(0)
# os._exit(0)


def invoke_command(to_call, *args, **kwargs):
Expand Down
5 changes: 4 additions & 1 deletion sickbeard/webserveInit.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

from sickbeard.helpers import create_https_certificates

app = None


def initWebServer(options={}):
options.setdefault('port', 8081)
Expand Down Expand Up @@ -149,6 +151,7 @@ def http_error_404_hander(status, message, traceback, version):
'tools.staticdir.dir': 'css'
},
}
global app
app = cherrypy.tree.mount(WebInterface(), options['web_root'], conf)

# auth
Expand Down Expand Up @@ -201,4 +204,4 @@ def autoproxy(
)

cherrypy.server.start()
cherrypy.server.wait()
#cherrypy.server.wait()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of these changes?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found that the .wait() method doesn't take any real effect beyond delaying the time the service comes up to an available state. Is there a defined reason to delay here?