'label'=>'lang:video_comment',
'rules'=>'trim|required|xss_clean|callback__is_user_loggedin'
)
+ ),
+ 'upload'=> array(
+ array(
+ 'field'=>'video-upload-file',
+ 'label'=>'lang:video_upload_file',
+ 'rules'=>'callback__required_upload|callback__valid_upload'
+ ),
+ array(
+ 'field'=>'video-title',
+ 'label'=>'lang:video_title',
+ 'rules'=>'trim|required|xss_clean'
+ ),
+ array(
+ 'field'=>'video-description',
+ 'label'=>'lang:video_description',
+ 'rules'=>'trim|required|xss_clean'
+ ),
+ array(
+ 'field'=>'video-tags',
+ 'label'=>'lang:video_tags',
+ 'rules'=>'required|callback__valid_tags'
+ )
)
);
| like account activation e-mail or password recovery.
|
*/
-$config['noreply_email'] = 'no-reply@p2p-next.cs.pub.ro';
\ No newline at end of file
+$config['noreply_email'] = 'no-reply@p2p-next.cs.pub.ro';
$this->load->view('footer');
$this->load->view('html_end');
}
+
+// public function upload()
+// {
+// $this->load->library('form_validation');
+//
+// $this->form_validation->set_error_delimiters('<span class="error">',
+// '</span>');
+// $error_upload = '';
+//
+// if ($this->form_validation->run('upload'))
+// {
+// if ($_FILES['video-upload-file']['tmp_name'])
+// {
+// // Upload library
+// $config_upload['upload_path'] = './data/upload';
+// $this->load->library('upload', $config_upload);
+//
+// $b_validation = $this->upload->do_upload('video-upload-file');
+// $error_upload =
+// $this->upload->display_errors('<span class="error">',
+// '</span>');
+// }
+// else
+// {
+// $b_validation = FALSE;
+// }
+// }
+// else
+// $b_validation = FALSE;
+//
+// if ($b_validation === FALSE)
+// {
+// $params = array('title' =>
+// $this->lang->line('ui_nav_menu_upload')
+// .' – '
+// . $this->config->item('site_name'),
+// //'metas' => array('description'=>'')
+// );
+// $this->load->library('html_head_params', $params);
+//
+// // **
+// // ** LOADING VIEWS
+// // **
+// $this->load->view('html_begin', $this->html_head_params);
+// $this->load->view('header',
+// array('selected_menu' => 'upload'));
+//
+// $main_params['content'] = $this->load->view(
+// 'video/upload_view',
+// array('error_upload'=> $error_upload),
+// TRUE);
+// $main_params['side'] = $this->load->view('side_default', NULL, TRUE);
+// $this->load->view('main', $main_params);
+//
+// $this->load->view('footer');
+// $this->load->view('html_end');
+// }
+// else
+// {
+//
+// }
+// }
+
+ public function upload()
+ {
+ $this->load->library('form_validation');
+
+ $this->form_validation->set_error_delimiters('<span class="error">',
+ '</span>');
+ $error_upload = '';
+
+ if ($this->form_validation->run('upload') === FALSE)
+ {
+ $params = array('title' =>
+ $this->lang->line('ui_nav_menu_upload')
+ .' – '
+ . $this->config->item('site_name'),
+ //'metas' => array('description'=>'')
+ );
+ $this->load->library('html_head_params', $params);
+
+ // **
+ // ** LOADING VIEWS
+ // **
+ $this->load->view('html_begin', $this->html_head_params);
+ $this->load->view('header',
+ array('selected_menu' => 'upload'));
+
+ $main_params['content'] = $this->load->view(
+ 'video/upload_view',
+ array('error_upload'=> $error_upload),
+ TRUE);
+ $main_params['side'] = $this->load->view('side_default', NULL, TRUE);
+ $this->load->view('main', $main_params);
+
+ $this->load->view('footer');
+ $this->load->view('html_end');
+ }
+ else
+ {
+
+ }
+ }
/**
* Increments (dis)likes count for video with the specified id and returns to
$this->videos_model->comment_video($video_id, $user_id, $comment);
}
+ public function _valid_tags($tags)
+ {
+ $tok = strtok($tags, ',');
+ while ($tok != FALSE)
+ {
+ $tok = trim($tok);
+ if (!ctype_alnum($tok))
+ return FALSE;
+
+ $tok = strtok(',');
+ }
+
+ return TRUE;
+ }
+
+ public function _required_upload($file)
+ {
+ if ($_FILES['video-upload-file']['tmp_name'])
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ public function _valid_upload($file)
+ {
+ if ($_FILES['video-upload-file']['tmp_name'])
+ {
+ // Upload library
+ $config_upload = array();
+ $config_upload['upload_path'] = './data/upload';
+ $config_upload['allowed_types'] = '*';
+ $this->load->library('upload', $config_upload);
+
+ if ($this->upload->do_upload('video-upload-file'))
+ {
+ return TRUE;
+ }
+
+ $this->form_validation->set_message('_valid_upload',
+ $this->upload->display_errors('<span class="error">',
+ '</span>'));
+ }
+
+ return FALSE;
+ }
+
/**
* OBSOLETE: AJAX page which retrieves a video plugin.
*
// Comment Video
$lang['_is_user_loggedin'] = 'In order to comment a video you must be logged in.';
+// Upload Video
+$lang['_valid_tags'] = 'Tags are alphanumeric words separated by commas.';
+$lang['_required_upload'] = 'The %s field is required.';
+$lang['_valid_upload'] = 'The uploaded video file is not supported.';
+
/* End of file form_validation_lang.php */
/* Location: ./system/language/english/form_validation_lang.php */
\ No newline at end of file
$lang['ui_nav_menu_about'] = 'About';
$lang['ui_nav_menu_help'] = 'Help';
$lang['ui_nav_menu_contact'] = 'Contact';
+$lang['ui_nav_menu_upload'] = 'Upload';
$lang['ui_nav_menu_log_in'] = 'Log In';
$lang['ui_nav_menu_register'] = 'Register';
$lang['ui_nav_menu_account'] = 'Account';
$lang['ui_captcha_instructions'] = 'Please insert the text from the image below in order to demonstrate that you are a human:';
$lang['ui_change_captcha'] = 'Change CAPTCHA image';
+$lang['ui_required_fields'] = '* Required fields!';
+
/* End of file ui_lang.php */
/* Location: ./application/language/english/ui_lang.php */
\ No newline at end of file
$lang['user_registration_date'] = 'Registration Date';
$lang['user_last_login'] = 'Last Login Time';
-$lang['user_note_required_fields'] = '* Required fields!';
$lang['user_submit_register'] = 'Register';
$lang['user_submit_save'] = 'Save';
<?php
+// Watch
$lang['video_like'] = 'Like';
$lang['video_dislike'] = 'Dislike';
$lang['video_title_no_comments'] = 'No comments yet';
$lang['video_submit_post_comment'] = 'Post';
+
+// Upload
+$lang['video_upload_file'] = 'Video File';
+$lang['video_title'] = 'Title';
+$lang['video_description'] = 'Description';
+$lang['video_tags'] = 'Tags';
+$lang['video_tags_hint'] = 'comma separated tags';
+$lang['video_submit_upload'] = 'Upload';
+
+
/* End of file video_lang.php */
/* Location: ./application/language/english/video_lang.php */
\ No newline at end of file
<?php echo ($selected_menu == 'account' ? 'class="selected"' : '') ?>><?php echo $this->lang->line('ui_nav_menu_account') ?></a>
</li>
+ <li class="menu-right"><a href="<?php echo site_url('video/upload') ?>"
+ <?php echo ($selected_menu == 'upload' ? 'class="selected"' : '') ?>><?php echo $this->lang->line('ui_nav_menu_upload') ?></a>
+ </li>
+
<li class="menu-right"><span class="menu-greeting">
<?php echo $this->lang->line('ui_hello'). ', '. $username. '! ' ?></span>
</li>
<table class="form">
<tr>
<td></td>
- <td><span class="required"><?php echo $this->lang->line('user_note_required_fields') ?></span></td>
+ <td><span class="required"><?php echo $this->lang->line('ui_required_fields') ?></span></td>
</tr>
<tr><td></td><td> </td></tr>
--- /dev/null
+<?php echo form_open_multipart("video/upload") ?>
+<table class="form">
+ <tr>
+ <td></td>
+ <td><span class="required"><?php echo $this->lang->line('ui_required_fields') ?></span></td>
+ </tr>
+
+ <tr><td></td><td> </td></tr>
+
+ <tr>
+ <th><?php echo $this->lang->line('video_upload_file') ?> <span class="required">*</span> : </th>
+ <td><input type="file" name="video-upload-file" size="32" /></td>
+ </tr>
+ <tr><td></td><td><?php echo form_error('video-upload-file') ?></td></tr>
+ <!--<tr><td></td><td><?php echo $error_upload ?></td></tr>-->
+
+ <tr><td></td><td> </td></tr>
+
+ <tr>
+ <th><?php echo $this->lang->line('video_title') ?> <span class="required">*</span> : </th>
+ <td><input type="text" name="video-title" value="<?php echo set_value('video-title') ?>" size="64" /></td>
+ </tr>
+ <tr><td></td><td><?php echo form_error('video-title') ?></td></tr>
+
+ <tr>
+ <th><?php echo $this->lang->line('video_description') ?> <span class="required">*</span> : </th>
+ <td><textarea name="video-description" rows="4" cols="32"><?php echo set_value('video-description') ?></textarea></td>
+ </tr>
+ <tr><td></td><td><?php echo form_error('video-description') ?></td></tr>
+
+ <tr>
+ <th><?php echo $this->lang->line('video_tags') ?> <span class="required">*</span> : </th>
+ <td><input type="text" name="video-tags" value="<?php echo set_value('video-tags') ?>" size="16" /> (<?php echo $this->lang->line('video_tags_hint') ?>)</td>
+ </tr>
+ <tr><td></td><td><?php echo form_error('video-tags') ?></td></tr>
+
+ <tr><td></td><td> </td></tr>
+
+ <tr>
+ <td></td>
+ <td><input type="submit" value="<?php echo $this->lang->line('video_submit_upload') ?>" /></td>
+ </tr>
+</table>
+</form>
\ No newline at end of file
import re
import random
-import api_exceptions
+import cis_exceptions
import cis_util
class BaseTranscoder:
raise ValueError('Video display aspect ratio must be a float or a string like <den>:<num>.')
self.output_file = os.path.join(self.dest_path, self.name)
+ if os.path.exists(self.output_file):
+ raise cis_exceptions.FileAlreadyExistsException( \
+ 'file "%s" already exists' % self.output_file)
+
if v_resolution is not None:
self.output_file += '_'
self.output_file += v_resolution[(v_resolution.rindex('x')+1):]
""" 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 cis_exceptions.NotImplementedException("Container " + name \
+ "not implemented")
return self.containers[name]
""" 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 cis_exceptions.NotImplementedException("Audio Codec " + name \
+ "not implemented")
return self.a_codecs[name]
""" 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 cis_exceptions.NotImplementedException("Video Codec " + name \
+ "not implemented")
return self.v_codecs[name]
thumb_extracted = True
try:
self.extract_thumb(seek_pos, resolution, n_thumbs_extracted)
- except api_exceptions.ThumbExtractionException as e:
+ except cis_exceptions.ThumbExtractionException as e:
thumb_extracted = False
if thumb_extracted:
""" Returns the name required as output file name based on index. """
output_file_name = os.path.join(self.dest_path, self.name) \
+ '_t' + ("%02d" % index) + '.jpg'
+
+ if os.path.exists(output_file_name):
+ raise cis_exceptions.FileAlreadyExistsException( \
+ 'file "%s" already exists' % output_file_name)
+
return output_file_name
def get_video_duration(self):
"""
import base
-import api_exceptions
+import cis_exceptions
import subprocess
import re
import os
exit_code = p.wait()
if exit_code > 0:
- raise api_exceptions.TranscodingException( \
+ raise cis_exceptions.TranscodingException( \
'FFmpeg exited with code ' + str(exit_code) + '.')
log.close()
exit_code = p.wait()
if exit_code > 0:
- raise api_exceptions.ThumbExtractionException( \
+ raise cis_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( \
+ raise cis_exceptions.ThumbExtractionException( \
'FFmpeg created an empty file.')
def get_video_duration(self):
exit_code = p.wait()
if exit_code > 0:
- raise api_exceptions.AVInfoException( \
+ raise cis_exceptions.AVInfoException( \
'ffprobe exited with code ' + str(exit_code) + '.')
import base
import ftp_config
import socket
-import api_exceptions
+import cis_exceptions
import os
import logger
try:
self.ftp.cwd(self.remote_path)
except ftplib.error_perm as e:
- raise api_exceptions.FileTransferException( \
+ raise cis_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)
+
+ if os.path.exists(local_fn):
+ raise cis_exceptions.FileAlreadyExistsException( \
+ 'file "%s" already exists' % local_fn)
+
try:
file_local = open(local_fn, 'wb')
except IOError as e:
- raise api_exceptions.FileTransferException( \
+ raise cis_exceptions.FileTransferException( \
"Could not open local file '%s' for writing: %s" \
% (local_fn, repr(e)))
self.ftp.retrbinary('RETR %s' % crt_fn, file_local.write)
file_local.close()
except ftplib.error_perm as e:
- raise api_exceptions.FileTransferException( \
+ raise cis_exceptions.FileTransferException( \
"Could not get file '%s' from Web Server: %s" \
% (remote_fn, repr(e)))
try:
self.ftp.cwd(self.remote_path)
except ftplib.error_perm as e:
- raise api_exceptions.FileTransferException( \
+ raise cis_exceptions.FileTransferException( \
"Could not change remote directory '%s': %s" \
% (self.remote_path, repr(e)))
try:
file_local = open(local_fn, 'rb')
except IOError as e:
- raise api_exceptions.FileTransferException( \
+ raise cis_exceptions.FileTransferException( \
"Could not open local file '%s' for reading: %s" \
% (local_fn, repr(e)))
self.ftp.storbinary('STOR %s' % crt_fn, file_local)
file_local.close()
except ftplib.error_perm as e:
- raise api_exceptions.FileTransferException( \
+ raise cis_exceptions.FileTransferException( \
"Could not put file '%s' to Web Server: %s" \
% (local_fn, repr(e)))
self.session.remove_download(dl, remove_content)
logger.log_msg('torrent "%s" stopped' % torrent)
break
+
+ def get_torrent_list(self):
+ """
+ Returns a list of all torrents started.
+ """
+
+ downloads = self.session.get_downloads()
+ torrent_list = []
+
+ for dl in downloads:
+ tdef = dl.get_def()
+ torrent_list.append(tdef.get_name() + '.tstream')
+
+ return torrent_list
import web
import json
from web.wsgiserver import CherryPyWSGIServer
+import urllib
import config
import bt
import users
import logger
+import cis_exceptions
if config.SECURITY:
CherryPyWSGIServer.ssl_certificate = "cacert.pem"
for f in files:
os.unlink(os.path.join(path, f))
+ def notify_completion(self):
+ logger.log_msg('#%s: notifying web server about the job completion...'\
+ % self.job_id)
+
+ f = urllib.urlopen(config.WS_COMPLETION)
+ f.read()
+
def run(self):
while True:
job = Server.queue.get()
- self.job_id = job['id']
+ self.job_id = job['code']
# * TRANSFER RAW VIDEO IN
try:
self.transfer_in(job['raw_video'])
+ except cis_exceptions.FileAlreadyExistsException as e:
+ logger.log_msg('#%s: %s' \
+ % (job['code'], repr(e)), logger.LOG_LEVEL_ERROR)
+ continue
except Exception as e:
logger.log_msg('#%s: error while transferring in: %s' \
- % (job['id'], str(e)), logger.LOG_LEVEL_FATAL)
+ % (job['code'], repr(e)), logger.LOG_LEVEL_FATAL)
continue
# * TRANSCODE RAW VIDEO
try:
self.transcode(job['raw_video'], job['name'], \
job['transcode_configs'])
+ except cis_exceptions.FileAlreadyExistsException as e:
+ logger.log_msg('#%s: %s' \
+ % (job['code'], repr(e)), logger.LOG_LEVEL_ERROR)
+ continue
except Exception as e:
logger.log_msg('#%s: error while transcoding: %s' \
- % (job['id'], str(e)), logger.LOG_LEVEL_FATAL)
+ % (job['code'], repr(e)), logger.LOG_LEVEL_FATAL)
continue
# * EXTRACT THUMBNAIL IMAGES
try:
self.extract_thumbs(job['raw_video'], job['name'], \
job['thumbs'])
+ except cis_exceptions.FileAlreadyExistsException as e:
+ logger.log_msg('#%s: %s' \
+ % (job['code'], repr(e)), logger.LOG_LEVEL_ERROR)
+ continue
except Exception as e:
logger.log_msg( \
'#%s: error while extracting thumbnail images: %s' \
- % (job['id'], str(e)), logger.LOG_LEVEL_FATAL)
+ % (job['code'], repr(e)), logger.LOG_LEVEL_FATAL)
continue
# * CREATE TORRENTS AND START SEEDING OF TRANSCODED VIDEOS
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)
+ % (job['code'], repr(e)), logger.LOG_LEVEL_FATAL)
+ continue
+
+ # * NOTIFY WEB SERVER ABOUT CONTENT INGESTION COMPLETION
+ # TODO in the future web server should also be notified about errors
+ try:
+ self.notify_completion()
+ except Exception as e:
+ logger.log_msg(
+ '#%s: error while notifying web server about the job completion: %s' \
+ % (job['code'], repr(e)), logger.LOG_LEVEL_FATAL)
continue
# * CLEANUP RAW VIDEOS AND THUMBNAIL IMAGES
resp = {"load": Server.load}
web.header('Content-Type', 'application/json')
return json.dumps(resp)
+ elif request == 'get_torrent_list':
+ resp = Server.bit_torrent.get_torrent_list()
+ web.header('Content-Type', 'application/json')
+ return json.dumps(resp)
#elif request == 'shutdown':
- #sys.exit(0)
+ #exit(0)
elif request == 'test':
return ''
else:
web.badrequest()
- return ""
+ return ''
def POST(self, request):
def __str__(self):
return repr(self.value)
+class FileAlreadyExistsException(Exception):
+ pass
+
class TranscodingException(Exception):
pass
WS_THUMBS_PATH = 'devel/data/thumbs'
-# === BITTORRENT CONFIGURATIONS ===
+# === URLS ===
#BT_TRACKER = "http://p2p-next-10.grid.pub.ro:6969/announce"
+# BitTorrent tracker URL.
BT_TRACKER = 'http://p2p-next-10.grid.pub.ro:6969/announce'
+# Web server's URL for content ingestion completion.
+WS_COMPLETION = 'http://p2p-next-03.grid.pub.ro:8081/cis_completion'
+#WS_COMPLETION = 'http://koala.cs.pub.ro/video/cis_completion'
+# === CIS PATHS ===
RAW_VIDEOS_PATH = 'tmp/raw'
-MEDIA_PATH = 'tmp/media'
THUMBS_PATH = 'tmp/thumbs'
+MEDIA_PATH = '/home/p2p/export/p2p-tube/media'
# In a distributed file system for multi-CIS.
TORRENTS_PATH = '/home/p2p/export/p2p-tube/torrents'
{
- "id": "33",
+ "code": "33",
"raw_video": "test.ogv",
"name": "test",
"weight": 3,
fi
JSON_FILE="$1"
-CIS_URL="http://p2p-next-02.grid.pub.ro:8080/"
+CIS_URL="http://p2p-next-03.grid.pub.ro:8080/"
curl -H 'Content-Type: application/json' --data-binary @"$JSON_FILE" ${CIS_URL}ingest_content