cis: bugs fixed; start / stop / remove torrents commands implemented
authorCălin-Andrei Burloiu <calin.burloiu@gmail.com>
Thu, 9 Feb 2012 16:28:26 +0000 (18:28 +0200)
committerCălin-Andrei Burloiu <calin.burloiu@gmail.com>
Thu, 9 Feb 2012 16:28:26 +0000 (18:28 +0200)
cis/api/avhandling.py [deleted file]
cis/api/base.py
cis/api/ffmpeg.py
cis/api/file_transfer.py [deleted file]
cis/api/ftp.py
cis/api/ftp_config.py
cis/bt.py
cis/cis.py [moved from cis/cisd.py with 71% similarity]
cis/config.py
cis/post-json.sh

diff --git a/cis/api/avhandling.py b/cis/api/avhandling.py
deleted file mode 100644 (file)
index 27c9a5d..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-#!/usr/bin/env python
-
-
-"""
-Classes derived from BaseTranscoder and BaseThumbExtractor for transcoding of
-videos and thumbnail extraction from videos using FFmpeg CLI program.
-"""
-
-import base
-import api_exceptions
-import subprocess
-import re
-import os
-import math
-
-class FFmpegTranscoder(base.BaseTranscoder):
-    """
-    FFmpeg CLI API for video transcoding.
-    """
-
-    prog_bin = "ffmpeg"
-
-    log_file = 'log/FFmpegTranscoder.log'
-
-    containers = {
-        "avi": "avi",
-        "flv": "flv",
-        "mp4": "mp4",
-        "ogg": "ogg",
-        "webm": "webm",
-        "mpegts": "mpegts"
-    }
-    a_codecs = {
-        "mp3": "libmp3lame",
-        "vorbis": "libvorbis"
-    }
-    v_codecs = {
-        "h264": "libx264",
-        "theora": "libtheora",
-        "vp8": "libvpx"
-    }
-
-    def _transcode(self, container, a_codec=None, v_codec=None,
-            a_bitrate=None, a_samplingrate=None, a_channels=None,
-            v_bitrate=None, v_framerate=None, v_resolution=None, v_dar=None):
-
-        args = self.prog_bin + ' -y -i "' + self.input_file + '" -f ' + container
-        
-        # Audio
-        if a_codec != None:
-            args += ' -acodec ' + a_codec
-            if a_bitrate != None:
-                args += ' -ab ' + str(a_bitrate)
-            if a_samplingrate != None:
-                args += ' -ar ' + str(a_samplingrate)
-            if a_channels != None:
-                args += ' -ac ' + str(a_channels)
-        
-        # Video
-        if v_codec != None:
-            args += ' -vcodec ' + v_codec
-            # Video codec specific options.
-            if v_codec == 'libx264':
-                args += ' -vpre normal'
-            if v_bitrate != None:
-                args += ' -b ' + str(v_bitrate)
-            if v_framerate != None:
-                args += ' -r ' + str(v_framerate)
-            if v_resolution != None:
-                args += ' -s ' + v_resolution
-            if v_dar != None:
-                args += ' -aspect ' + v_dar
-        
-        # Output file.
-        args += ' "' + self.output_file + '"'
-            
-        # READ handler for process's output.
-        p = subprocess.Popen(args, shell=True, 
-                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-        pipe = p.stdout
-
-        # WRITE handler for logging.
-        log = open(self.log_file, 'w')
-        log.write(args + '\n')
-
-        while True:
-            line = pipe.readline()
-            if len(line) == 0:
-                break
-            log.write(line)
-
-        exit_code = p.wait()
-        if exit_code > 0:
-            raise api_exceptions.TranscodingException( \
-                    'FFmpeg exited with code ' + str(exit_code) + '.')
-
-        log.close()
-
-        return self.output_file
-
-
-class FFmpegThumbExtractor(base.BaseThumbExtractor):
-    """
-    FFmpeg CLI API for video thumbnail extraction.
-    """
-
-    prog_bin = "ffmpeg"
-
-    log_file = 'log/FFmpegThumbExtractor.log'
-
-    def extract_thumb(self, seek_pos, resolution="120x90", index=0):
-        output_file = self.get_output_file_name(index)
-
-        args = self.prog_bin + ' -y -i "' + self.input_file \
-                + '" -f rawvideo -vcodec mjpeg' + (' -ss ' + str(seek_pos)) \
-                + " -vframes 1 -an -s " + resolution + ' "' \
-                + output_file + '"'
-
-        # READ handler for process's output.
-        p = subprocess.Popen(args, shell=True,
-                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-        pipe = p.stdout
-        
-        # WRITE handler for logging.
-        log = open(self.log_file, 'w')
-        log.write(args + '\n')
-
-        while True:
-            line = pipe.readline()
-            if len(line) == 0:
-                break
-            log.write(line)
-
-        exit_code = p.wait()
-        if exit_code > 0:
-            raise api_exceptions.ThumbExtractionException( \
-                    'FFmpeg exited with code ' + str(exit_code) + '.')
-
-        # FFmpeg bug: when no key frame is found from seek_pos to the
-        # end of file an empty image file is created.
-        if os.path.getsize(output_file) == 0L:
-            os.unlink(output_file)
-            raise api_exceptions.ThumbExtractionException( \
-                    'FFmpeg created an empty file.')
-
-    def get_video_duration(self):
-        return FFprobeAVInfo.get_video_duration(self.input_file)
-
-
-class FFprobeAVInfo(base.BaseAVInfo):
-    
-    prog_bin = "ffprobe"
-
-    log_file = 'log/FFprobeAVInfo.log'
-
-    @staticmethod
-    def get_video_duration(input_file, formated=False):
-        args = FFprobeAVInfo.prog_bin + ' -show_format "' \
-                + input_file + '"'
-
-        # READ handler for process's output.
-        p = subprocess.Popen(args, shell=True,
-                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-        pipe = p.stdout
-
-        # WRITE handler for logging.
-        log = open(FFprobeAVInfo.log_file, 'w')
-        log.write(args + '\n')
-
-        # Parse ffprobe's output.
-        while True:
-            line = pipe.readline()
-            if len(line) == 0:
-                break
-            log.write(line)
-            
-            # Search for the line which contains duration information.
-            m = re.match(r"duration=([\d\.]+)", line)
-            if m is not None:
-                seconds = float(m.group(1))
-                if not formated:
-                    return seconds
-                else:
-                    seconds = math.floor(seconds)
-                    minutes = math.floor(seconds / 60)
-                    seconds = seconds % 60
-                    if minutes >= 60:
-                        hours = math.floor(minutes / 60)
-                        minutes = minutes % 60
-                        
-                        return "%02d:%02d:%02d" % (hours, minutes, seconds)
-                    else:
-                        return "%02d:%02d" % (minutes, seconds)
-
-        exit_code = p.wait()
-        if exit_code > 0:
-            raise api_exceptions.AVInfoException( \
-                    'ffprobe exited with code ' + str(exit_code) + '.')
index 0b30bc9..500aada 100644 (file)
@@ -137,7 +137,8 @@ class BaseTranscoder:
         """ Translates container API name into external program identifier."""
 
         if not self.containers.has_key(name) or self.containers[name] is None:
-            raise api_exceptions.NotImplementedException("Container " + name)
+            raise api_exceptions.NotImplementedException("Container " + name \
+                    + "not implemented")
 
         return self.containers[name]
 
@@ -158,7 +159,8 @@ class BaseTranscoder:
         """ Translates audio codec API name into external program identifier."""
 
         if not self.a_codecs.has_key(name) or self.a_codecs[name] is None:
-            raise api_exceptions.NotImplementedException("Audio Codec " + name)
+            raise api_exceptions.NotImplementedException("Audio Codec " + name \
+                    + "not implemented")
 
         return self.a_codecs[name]
 
@@ -166,7 +168,8 @@ class BaseTranscoder:
         """ Translates video codec API name into external program identifier."""
 
         if not self.v_codecs.has_key(name) or self.v_codecs[name] is None:
-            raise api_exceptions.NotImplementedException("Video Codec " + name)
+            raise api_exceptions.NotImplementedException("Video Codec " + name \
+                    + "not implemented")
 
         return self.v_codecs[name]
 
index e69de29..27c9a5d 100644 (file)
@@ -0,0 +1,198 @@
+#!/usr/bin/env python
+
+
+"""
+Classes derived from BaseTranscoder and BaseThumbExtractor for transcoding of
+videos and thumbnail extraction from videos using FFmpeg CLI program.
+"""
+
+import base
+import api_exceptions
+import subprocess
+import re
+import os
+import math
+
+class FFmpegTranscoder(base.BaseTranscoder):
+    """
+    FFmpeg CLI API for video transcoding.
+    """
+
+    prog_bin = "ffmpeg"
+
+    log_file = 'log/FFmpegTranscoder.log'
+
+    containers = {
+        "avi": "avi",
+        "flv": "flv",
+        "mp4": "mp4",
+        "ogg": "ogg",
+        "webm": "webm",
+        "mpegts": "mpegts"
+    }
+    a_codecs = {
+        "mp3": "libmp3lame",
+        "vorbis": "libvorbis"
+    }
+    v_codecs = {
+        "h264": "libx264",
+        "theora": "libtheora",
+        "vp8": "libvpx"
+    }
+
+    def _transcode(self, container, a_codec=None, v_codec=None,
+            a_bitrate=None, a_samplingrate=None, a_channels=None,
+            v_bitrate=None, v_framerate=None, v_resolution=None, v_dar=None):
+
+        args = self.prog_bin + ' -y -i "' + self.input_file + '" -f ' + container
+        
+        # Audio
+        if a_codec != None:
+            args += ' -acodec ' + a_codec
+            if a_bitrate != None:
+                args += ' -ab ' + str(a_bitrate)
+            if a_samplingrate != None:
+                args += ' -ar ' + str(a_samplingrate)
+            if a_channels != None:
+                args += ' -ac ' + str(a_channels)
+        
+        # Video
+        if v_codec != None:
+            args += ' -vcodec ' + v_codec
+            # Video codec specific options.
+            if v_codec == 'libx264':
+                args += ' -vpre normal'
+            if v_bitrate != None:
+                args += ' -b ' + str(v_bitrate)
+            if v_framerate != None:
+                args += ' -r ' + str(v_framerate)
+            if v_resolution != None:
+                args += ' -s ' + v_resolution
+            if v_dar != None:
+                args += ' -aspect ' + v_dar
+        
+        # Output file.
+        args += ' "' + self.output_file + '"'
+            
+        # READ handler for process's output.
+        p = subprocess.Popen(args, shell=True, 
+                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        pipe = p.stdout
+
+        # WRITE handler for logging.
+        log = open(self.log_file, 'w')
+        log.write(args + '\n')
+
+        while True:
+            line = pipe.readline()
+            if len(line) == 0:
+                break
+            log.write(line)
+
+        exit_code = p.wait()
+        if exit_code > 0:
+            raise api_exceptions.TranscodingException( \
+                    'FFmpeg exited with code ' + str(exit_code) + '.')
+
+        log.close()
+
+        return self.output_file
+
+
+class FFmpegThumbExtractor(base.BaseThumbExtractor):
+    """
+    FFmpeg CLI API for video thumbnail extraction.
+    """
+
+    prog_bin = "ffmpeg"
+
+    log_file = 'log/FFmpegThumbExtractor.log'
+
+    def extract_thumb(self, seek_pos, resolution="120x90", index=0):
+        output_file = self.get_output_file_name(index)
+
+        args = self.prog_bin + ' -y -i "' + self.input_file \
+                + '" -f rawvideo -vcodec mjpeg' + (' -ss ' + str(seek_pos)) \
+                + " -vframes 1 -an -s " + resolution + ' "' \
+                + output_file + '"'
+
+        # READ handler for process's output.
+        p = subprocess.Popen(args, shell=True,
+                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        pipe = p.stdout
+        
+        # WRITE handler for logging.
+        log = open(self.log_file, 'w')
+        log.write(args + '\n')
+
+        while True:
+            line = pipe.readline()
+            if len(line) == 0:
+                break
+            log.write(line)
+
+        exit_code = p.wait()
+        if exit_code > 0:
+            raise api_exceptions.ThumbExtractionException( \
+                    'FFmpeg exited with code ' + str(exit_code) + '.')
+
+        # FFmpeg bug: when no key frame is found from seek_pos to the
+        # end of file an empty image file is created.
+        if os.path.getsize(output_file) == 0L:
+            os.unlink(output_file)
+            raise api_exceptions.ThumbExtractionException( \
+                    'FFmpeg created an empty file.')
+
+    def get_video_duration(self):
+        return FFprobeAVInfo.get_video_duration(self.input_file)
+
+
+class FFprobeAVInfo(base.BaseAVInfo):
+    
+    prog_bin = "ffprobe"
+
+    log_file = 'log/FFprobeAVInfo.log'
+
+    @staticmethod
+    def get_video_duration(input_file, formated=False):
+        args = FFprobeAVInfo.prog_bin + ' -show_format "' \
+                + input_file + '"'
+
+        # READ handler for process's output.
+        p = subprocess.Popen(args, shell=True,
+                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        pipe = p.stdout
+
+        # WRITE handler for logging.
+        log = open(FFprobeAVInfo.log_file, 'w')
+        log.write(args + '\n')
+
+        # Parse ffprobe's output.
+        while True:
+            line = pipe.readline()
+            if len(line) == 0:
+                break
+            log.write(line)
+            
+            # Search for the line which contains duration information.
+            m = re.match(r"duration=([\d\.]+)", line)
+            if m is not None:
+                seconds = float(m.group(1))
+                if not formated:
+                    return seconds
+                else:
+                    seconds = math.floor(seconds)
+                    minutes = math.floor(seconds / 60)
+                    seconds = seconds % 60
+                    if minutes >= 60:
+                        hours = math.floor(minutes / 60)
+                        minutes = minutes % 60
+                        
+                        return "%02d:%02d:%02d" % (hours, minutes, seconds)
+                    else:
+                        return "%02d:%02d" % (minutes, seconds)
+
+        exit_code = p.wait()
+        if exit_code > 0:
+            raise api_exceptions.AVInfoException( \
+                    'ffprobe exited with code ' + str(exit_code) + '.')
diff --git a/cis/api/file_transfer.py b/cis/api/file_transfer.py
deleted file mode 100644 (file)
index 2f33eac..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/usr/bin/env python
-
-"""
-Classes that facilitate file transfer (between Web Server and CIS).
-
-They may extend BaseFileTransferer class.
-"""
-
-import sys
-import ftplib
-import base
-import ftp_config
-import socket
-import api_exceptions
-import os
-
-
-class FTPFileTransferer(base.BaseFileTransferer):
-    """
-    FTPS implementation for file transfering between Web Server and CIS.
-    """
-
-    ftp = None
-
-    def __init__(self, local_path='', remote_path=''):
-        base.BaseFileTransferer.__init__(self, local_path, remote_path)
-
-        self.ftp = ftplib.FTP_TLS(ftp_config.FTP_HOST, ftp_config.FTP_USER,
-                ftp_config.FTP_PASSWD, ftp_config.FTP_ACCT)
-        self.ftp.set_pasv(True)
-
-    def get(self, files):
-        try:
-            self.ftp.cwd(self.remote_path)
-        except ftplib.error_perm as e:
-            raise api_exceptions.FileTransferException( \
-                    "Could not change remote directory '%s': %s" \
-                    % (self.remote_path, repr(e)))
-
-
-        for crt_fn in files:
-            local_fn = os.path.join(self.local_path, crt_fn)
-            remote_fn = os.path.join(self.remote_path, crt_fn)
-            try:
-                file_local = open(local_fn, 'wb')
-            except IOError as e:
-                raise api_exceptions.FileTransferException( \
-                        "Could not open local file '%s' for writing: %s" \
-                        % (local_fn, repr(e)))
-
-            try:
-                self.ftp.retrbinary('RETR %s' % crt_fn, file_local.write)
-                file_local.close()
-            except ftplib.error_perm as e:
-                raise api_exceptions.FileTransferException( \
-                        "Could not get file '%s' from Web Server: %s" \
-                        % (remote_fn, repr(e)))
-
-    def put(self, files):
-        try:
-            self.ftp.cwd(self.remote_path)
-        except ftplib.error_perm as e:
-            raise api_exceptions.FileTransferException( \
-                    "Could not change remote directory '%s': %s" \
-                    % (self.remote_path, repr(e)))
-
-        for crt_fn in files:
-            local_fn = os.path.join(self.local_path, crt_fn)
-
-            try:
-                file_local = open(local_fn, 'rb')
-            except IOError as e:
-                raise api_exceptions.FileTransferException( \
-                        "Could not open local file '%s' for reading: %s" \
-                        % (local_fn, repr(e)))
-                
-            try:
-                self.ftp.storbinary('STOR %s' % crt_fn, file_local)
-                file_local.close()
-            except ftplib.error_perm as e:
-                raise api_exceptions.FileTransferException( \
-                        "Could not put file '%s' to Web Server: %s" \
-                        % (local_fn, repr(e)))
-
-    def close(self):
-        if self.ftp is not None:
-            try:
-                self.ftp.quit()
-            except:
-                pass
index e69de29..567978f 100644 (file)
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+
+"""
+Classes that facilitate file transfer (between Web Server and CIS).
+
+They may extend BaseFileTransferer class.
+"""
+
+import sys
+import ftplib
+import base
+import ftp_config
+import socket
+import api_exceptions
+import os
+
+import logger
+
+
+class FTPFileTransferer(base.BaseFileTransferer):
+    """
+    FTPS implementation for file transfering between Web Server and CIS.
+    """
+
+    ftp = None
+
+    def __init__(self, local_path='', remote_path=''):
+        base.BaseFileTransferer.__init__(self, local_path, remote_path)
+
+        self.ftp = ftplib.FTP_TLS(ftp_config.FTP_HOST, ftp_config.FTP_USER,
+                ftp_config.FTP_PASSWD, ftp_config.FTP_ACCT)
+        self.ftp.set_pasv(True)
+
+    def get(self, files):
+        try:
+            self.ftp.cwd(self.remote_path)
+        except ftplib.error_perm as e:
+            raise api_exceptions.FileTransferException( \
+                    "Could not change remote directory '%s': %s" \
+                    % (self.remote_path, repr(e)))
+
+
+        for crt_fn in files:
+            local_fn = os.path.join(self.local_path, crt_fn)
+            remote_fn = os.path.join(self.remote_path, crt_fn)
+            try:
+                file_local = open(local_fn, 'wb')
+            except IOError as e:
+                raise api_exceptions.FileTransferException( \
+                        "Could not open local file '%s' for writing: %s" \
+                        % (local_fn, repr(e)))
+
+            try:
+                self.ftp.retrbinary('RETR %s' % crt_fn, file_local.write)
+                file_local.close()
+            except ftplib.error_perm as e:
+                raise api_exceptions.FileTransferException( \
+                        "Could not get file '%s' from Web Server: %s" \
+                        % (remote_fn, repr(e)))
+
+    def put(self, files):
+        try:
+            self.ftp.cwd(self.remote_path)
+        except ftplib.error_perm as e:
+            raise api_exceptions.FileTransferException( \
+                    "Could not change remote directory '%s': %s" \
+                    % (self.remote_path, repr(e)))
+
+        for crt_fn in files:
+            local_fn = os.path.join(self.local_path, crt_fn)
+
+            try:
+                file_local = open(local_fn, 'rb')
+            except IOError as e:
+                raise api_exceptions.FileTransferException( \
+                        "Could not open local file '%s' for reading: %s" \
+                        % (local_fn, repr(e)))
+                
+            try:
+                self.ftp.storbinary('STOR %s' % crt_fn, file_local)
+                file_local.close()
+            except ftplib.error_perm as e:
+                raise api_exceptions.FileTransferException( \
+                        "Could not put file '%s' to Web Server: %s" \
+                        % (local_fn, repr(e)))
+
+    def close(self):
+        if self.ftp is not None:
+            try:
+                self.ftp.quit()
+            except:
+                pass
index 551cd7d..e00e3fe 100644 (file)
@@ -2,7 +2,7 @@
 Configuration file for FTPFileTransferer class.
 """
 
-FTP_HOST = "localhost"
-FTP_USER = "calinburloiu"
-FTP_PASSWD = "Ps1st1h14_"
+FTP_HOST = "koala.cs.pub.ro"
+FTP_USER = "ftp-p2p-next"
+FTP_PASSWD = "student"
 FTP_ACCT = ""
index 9281876..74d43ab 100644 (file)
--- a/cis/bt.py
+++ b/cis/bt.py
@@ -49,9 +49,10 @@ class BitTorrent:
         
         self.session = Session(sscfg)
 
-    def start_download(self, torrent, output_dir='.'):
+    def start_torrent(self, torrent, output_dir='.'):
         """
         Download (leech or seed) a file via BitTorrent.
+        
         The code is adapted from Next-Share's 'BaseLib/Tools/cmdlinedl.py'.
 
         @param torrent .torrent file or URL
@@ -80,3 +81,23 @@ class BitTorrent:
         #else:
             #logger.log_msg('download of torrent "%s" already started' \
                     #% torrent, logger.LOG_LEVEL_DEBUG)
+    
+    def stop_torrent(self, torrent, remove_content=False):
+        """
+        Stop leeching or seeding a file via BitTorrent.
+        
+        !!! Only tested with torrents started with .tstream files. Not tested
+        for torrents started with URLs.
+        
+        @param torrent .torrent file or URL
+        @param remove_content removes downloaded file
+        """
+        
+        downloads = self.session.get_downloads()
+        
+        for dl in downloads:
+            tdef = dl.get_def()
+            if torrent.find(tdef.get_name()) == 0:
+                self.session.remove_download(dl, remove_content)
+                logger.log_msg('torrent "%s" stopped' % torrent)
+                break
similarity index 71%
rename from cis/cisd.py
rename to cis/cis.py
index 2f39a6e..f88aad5 100755 (executable)
@@ -46,7 +46,7 @@ class CIWorker(threading.Thread):
         logger.log_msg('#%s: transfering in...' % self.job_id)
         
         file_transfer = config.FILE_TRANSFERER_CLASS( \
-                config.RAW_VIDEOS_PATH, config.WS_UPLOAD_PATH)
+                config.RAW_VIDEOS_PATH, config.WS_UPLOAD_PATH)            
         file_transfer.get([raw_video])
         file_transfer.close()
 
@@ -120,7 +120,7 @@ class CIWorker(threading.Thread):
             output_file = output_file[(output_file.rindex('/') + 1):]
 
             # * SEED TORRENTS
-            Server.bit_torrent.start_download( \
+            Server.bit_torrent.start_torrent( \
                     os.path.join(config.TORRENTS_PATH, output_file),
                     config.MEDIA_PATH)
                     
@@ -155,16 +155,32 @@ class CIWorker(threading.Thread):
             self.job_id = job['id']
 
             # * TRANSFER RAW VIDEO IN
-            self.transfer_in(job['raw_video'])
+            try:
+                self.transfer_in(job['raw_video'])
+            except Exception as e:
+                logger.log_msg('#%s: error while transferring in: %s' \
+                        % (job['id'], str(e)), logger.LOG_LEVEL_FATAL) 
+                continue
 
             # * TRANSCODE RAW VIDEO
-            self.transcode(job['raw_video'], job['name'], \
-                    job['transcode_configs'])
+            try:
+                self.transcode(job['raw_video'], job['name'], \
+                        job['transcode_configs'])
+            except Exception as e:
+                logger.log_msg('#%s: error while transcoding: %s' \
+                        % (job['id'], str(e)), logger.LOG_LEVEL_FATAL) 
+                continue
 
             # * EXTRACT THUMBNAIL IMAGES
             if job['thumbs'] != 0:
-                self.extract_thumbs(job['raw_video'], job['name'], \
-                        job['thumbs'])
+                try:
+                    self.extract_thumbs(job['raw_video'], job['name'], \
+                            job['thumbs'])
+                except Exception as e:
+                    logger.log_msg( \
+                            '#%s: error while extracting thumbnail images: %s' \
+                            % (job['id'], str(e)), logger.LOG_LEVEL_FATAL) 
+                    continue
 
             # * CREATE TORRENTS AND START SEEDING OF TRANSCODED VIDEOS
             self.seed(job['transcode_configs'])
@@ -182,10 +198,15 @@ class CIWorker(threading.Thread):
             thumb_files = fnmatch.filter(files, job['name'] + "_*")
 
             # * TRANSFER TORRENTS AND THUMBNAIL IMAGES OUT
-            self.transfer_out(torrent_files, config.TORRENTS_PATH, \
-                    config.WS_TORRENTS_PATH)
-            self.transfer_out(thumb_files, config.THUMBS_PATH, \
-                    config.WS_THUMBS_PATH)
+            try:
+                self.transfer_out(torrent_files, config.TORRENTS_PATH, \
+                        config.WS_TORRENTS_PATH)
+                self.transfer_out(thumb_files, config.THUMBS_PATH, \
+                        config.WS_THUMBS_PATH)
+            except Exception as e:
+                logger.log_msg('#%s: error while transferring out: %s' \
+                        % (job['id'], str(e)), logger.LOG_LEVEL_FATAL) 
+                continue
             
             # * CLEANUP RAW VIDEOS AND THUMBNAIL IMAGES
             self.remove_files([ job['raw_video'] ], config.RAW_VIDEOS_PATH)
@@ -215,6 +236,8 @@ class Server:
             resp = {"load": Server.load}
             web.header('Content-Type', 'application/json')
             return json.dumps(resp)
+        #elif request == 'shutdown':
+            #sys.exit(0)
         elif request == 'test':
             return ''
         else:
@@ -240,26 +263,55 @@ class Server:
             Server.queue.put(data)
 
             return 'Job submitted.'
+        elif request == 'start_torrents':
+            # Read JSON parameters.
+            json_data = web.data()
+            data = json.loads(json_data)
+            
+            # TODO Verify data
+            Server.start_torrents(data)
+        elif request == 'stop_torrents':
+            # Read JSON parameters.
+            json_data = web.data()
+            data = json.loads(json_data)
+            
+            # TODO Verify data
+            Server.stop_torrents(data)
+        elif request == 'remove_torrents':
+            # Read JSON parameters.
+            json_data = web.data()
+            data = json.loads(json_data)
+            
+            # TODO Verify data
+            Server.stop_torrents(data, True)
         else:
             web.badrequest()
             return ""
     
     @staticmethod
-    def start_downloads():
+    def start_torrents(torrents=None):
+        """
+        Scans torrent path for files in order to start download for the files
+        that are not already started.
+        """
+        
         # All torrent files.
-        files = [f for f in os.listdir(config.TORRENTS_PATH) \
-                if os.path.isfile(os.path.join( \
-                        config.TORRENTS_PATH, f))]
-        torrent_files = fnmatch.filter(files, "*.tstream")
-
-        for torrent_file in torrent_files:
-            Server.bit_torrent.start_download( \
-                    torrent_file,
+        if torrents == None:
+            files = [f for f in os.listdir(config.TORRENTS_PATH) \
+                    if os.path.isfile(os.path.join( \
+                            config.TORRENTS_PATH, f))]
+            torrents = fnmatch.filter(files, "*.tstream")
+
+        for torrent_file in torrents:
+            Server.bit_torrent.start_torrent( \
+                    os.path.join(config.TORRENTS_PATH, torrent_file),
                     config.MEDIA_PATH)
-        
-        t = threading.Timer(config.START_DOWNLOADS_INTERVAL, \
-                Server.start_downloads)
-        t.start()
+    
+    @staticmethod
+    def stop_torrents(torrents, remove_content=False):
+        for torrent_file in torrents:
+            Server.bit_torrent.stop_torrent( \
+                    torrent_file, remove_content)
 
     def authenticate(self, username, password):
         if not config.SECURITY:
@@ -278,7 +330,11 @@ if __name__ == '__main__':
     Server.queue = Queue()
     Server.load = 0
     
-    Server.start_downloads()
+    Server.start_torrents()
+    t = threading.Timer(config.START_DOWNLOADS_INTERVAL, \
+            Server.start_torrents)
+    t.daemon = True
+    t.start()
     
     # Worker thread.
     ci_worker = CIWorker()
index c68d05c..094e552 100644 (file)
@@ -3,8 +3,8 @@
 import logger
 
 # Make here all necessary imports required for API classes.
-from api import avhandling
-from api import file_transfer
+from api import ffmpeg
+from api import ftp
 
 
 # === GENERAL CONFIGURATIONS ===
@@ -15,34 +15,35 @@ START_DOWNLOADS_INTERVAL = 24 * 3600.0 # Once a day
 
 # === FILE TRANSFER CONFIGURATIONS ===
 # Path from the Web Server where the raw input video file is stored.
-WS_UPLOAD_PATH = 'tmp/data/upload'
+WS_UPLOAD_PATH = 'devel/data/upload'
 # Path from the Web Server where the output torrent files will be stored.
-WS_TORRENTS_PATH = 'tmp/data/torrents'
+WS_TORRENTS_PATH = 'devel/data/torrents'
 # Path from the Web Server where the output thumbnail image files will be
 # stored.
-WS_THUMBS_PATH = 'tmp/data/thumbs'
+WS_THUMBS_PATH = 'devel/data/thumbs'
 
 
 # === BITTORRENT CONFIGURATIONS ===
 #BT_TRACKER = "http://p2p-next-10.grid.pub.ro:6969/announce"
-BT_TRACKER = 'http://localhost:6969/announce'
+BT_TRACKER = 'http://p2p-next-10.grid.pub.ro:6969/announce'
 
 
 RAW_VIDEOS_PATH = 'tmp/raw'
 MEDIA_PATH = 'tmp/media'
 THUMBS_PATH = 'tmp/thumbs'
-TORRENTS_PATH = 'tmp/torrents' # In a distributed file system for multi-CIS
+# In a distributed file system for multi-CIS.
+TORRENTS_PATH = '/home/p2p/export/p2p-tube/torrents'
 
 
 # === EXTERNAL PROGRAMS API CLASSES ===
 # API class for a prgram which retrives audio/video information, like duration.
-AVINFO_CLASS = avhandling.FFprobeAVInfo
+AVINFO_CLASS = ffmpeg.FFprobeAVInfo
 # API class for a prgram which transcodes an audio/video file.
-TRANSCODER_CLASS = avhandling.FFmpegTranscoder
+TRANSCODER_CLASS = ffmpeg.FFmpegTranscoder
 # API class for a prgram which extracts thumbnail images from a file.
-THUMB_EXTRACTOR_CLASS = avhandling.FFmpegThumbExtractor
+THUMB_EXTRACTOR_CLASS = ffmpeg.FFmpegThumbExtractor
 # API class for a prgram which transfers files between Web Server and CIS.
-FILE_TRANSFERER_CLASS = file_transfer.FTPFileTransferer
+FILE_TRANSFERER_CLASS = ftp.FTPFileTransferer
 
 
 # === EXTERNAL PROGRAMS BINARY FILES ===
index eac61f7..1132520 100755 (executable)
@@ -6,5 +6,6 @@ if [ $# -ne 1 ]; then
 fi
 
 JSON_FILE="$1"
+CIS_URL="http://p2p-next-02.grid.pub.ro:8080/"
 
-curl -H application/json --data-binary @"$JSON_FILE" http://localhost:8080/ingest_content
+curl -H 'Content-Type: application/json' --data-binary @"$JSON_FILE" ${CIS_URL}ingest_content