Skip to content

Commit

Permalink
[*] Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
werwolfby committed Jul 17, 2015
0 parents commit 1e163c6
Show file tree
Hide file tree
Showing 12 changed files with 485 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
*.pyc
4 changes: 4 additions & 0 deletions plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

118 changes: 118 additions & 0 deletions plugins/lostfilm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import re
import requests
from requests import Session
from bs4 import BeautifulSoup
from sqlalchemy import Column, Integer, String, DateTime
from . import Base
from urlparse import urlparse, parse_qs


class LostFilmTVSeries(Base):
__tablename__ = "lostfilmtv_series"

id = Column(Integer, primary_key=True)
name = Column(String, unique=True, nullable=False)
url = Column(String, nullable=False)
season_number = Column(Integer, nullable=False)
episiode_number = Column(Integer, nullable=False)
last_update = Column(DateTime, nullable=True)


class LostFilmTVCredentials(Base):
__tablename__ = "lostfilmtv_credentials"

username = Column(String, primary_key=True)
password = Column(String, primary_key=True)
c_uid = Column('uid', String)
c_pass = Column('pass', String)
c_usess = Column('usess', String)


class LostFilmTVException(Exception):
pass


class LostFilmTVLoginFailedException(Exception):
def __init__(self, code, text, message):
self.code = code
self.text = text
self.message = message


class LostFilmTVTracker(object):
search_usess_re = re.compile(ur'\(usess=([a-f0-9]{32})\)', re.IGNORECASE)
login_url = "https://login1.bogi.ru/login.php?referer=https%3A%2F%2Fwww.lostfilm.tv%2F"
profile_url = 'http://www.lostfilm.tv/my.php'
netloc = 'www.lostfilm.tv'

def __init__(self, c_uid = None, c_pass = None, c_usess = None):
self.c_uid = c_uid
self.c_pass = c_pass
self.c_usess = c_usess

def login(self, username, password):
s = Session()
# login over bogi.ru
params = {"login": username, "password": password}
r1 = s.post(self.login_url, params, verify=False)
# in case of failed login, bogi redirects to:
# http://www.lostfilm.tv/blg.php?code=6&text=incorrect%20login/password
if r1.request.url != self.login_url:
url = urlparse(r1.url)
if url.netloc == self.netloc:
query = parse_qs(url.query)
code = int(query.get('code', ['-1'])[0])
text = query.get('text', "-")
r1.encoding = 'windows-1251'
message = r1.text
raise LostFilmTVLoginFailedException(code, text, message)
else:
raise LostFilmTVLoginFailedException(-1, None, None)

# callback to lostfilm.tv
soup = BeautifulSoup(r1.text)
inputs = soup.findAll("input")
action = soup.find("form")['action']
cparams = dict([(i['name'], i['value']) for i in inputs if 'value' in i.attrs])
s.post(action, cparams, verify=False)

# call to profile page
r3 = s.get(self.profile_url)

# read required params
self.c_uid = s.cookies['uid']
self.c_pass = s.cookies['pass']
self.c_usess = self.search_usess_re.findall(r3.text)[0]

def verify(self):
cookies = {'uid': self.c_uid, 'pass': self.c_pass, 'usess': self.c_usess}
r1 = requests.get('http://www.lostfilm.tv/my.php', cookies=cookies)
return len(r1.text) > 0


class LostFilmPlugin(object):
_regex = re.compile(ur'http://www\.lostfilm\.tv/browse\.php\?cat=\d+')
_search_usess_re = re.compile(ur'\(usess=([a-f0-9]{32})\)', re.IGNORECASE)

def __init__(self):
super(LostFilmPlugin, self).__init__()

def parse_url(self, url):
match = self._regex.match(url)
if match is None:
return None

r = requests.get(url, allow_redirects=False)
if r.status_code != 200:
return None
soup = BeautifulSoup(r.text)
title = soup.title.string.strip()
return title

# def walk(self, db):
# credentials = db.query(LostFilmTVCredentials).first()
# if credentials is None or not credentials.username or not credentials.password:
# return
# if not credentials.cookies or not self.verify(credentials.c_uid, credentials.c_pass, credentials.c_usess):
# self.login(credentials.username, credentials.password)
# pass
82 changes: 82 additions & 0 deletions server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import os

import cherrypy
from plugins.lostfilm import LostFilmPlugin

plugins = [
LostFilmPlugin()
]


class App(object):
def __init__(self):
super(App, self).__init__()
self.api = Api()

@cherrypy.expose
def index(self):
# return file('./static/index.html')
raise cherrypy.HTTPRedirect("/static/index.html")

class Api(object):
def __init__(self):
super(Api, self).__init__()
self.torrents = TorrentsApi()

@cherrypy.expose
def parse(self, url):
for plugin in plugins:
result = plugin.parse_url(url)
if result is not None:
return result
raise cherrypy.HTTPError(404, "Can't parse url %s" % url)


class TorrentsApi(object):
_torrents = [{'id': 1, 'name': u"Arrow"},
{'id': 2, 'name': u"Castle"},
{'id': 3, 'name': u"Cougar Town"},
{'id': 4, 'name': u"Extant"},
{'id': 5, 'name': u"Falling Skies"},
{'id': 6, 'name': u"Game of Thrones"},
{'id': 7, 'name': u"Gotham"},
{'id': 8, 'name': u"Grimm"},
{'id': 9, 'name': u"Hell on wheels"},
{'id': 10, 'name': u"Modern Family"},
{'id': 11, 'name': u"Supernatural"},
{'id': 12, 'name': u"The Flash"},
{'id': 13, 'name': u"The Hundred"},
{'id': 14, 'name': u"The Last Ship"},
{'id': 15, 'name': u"The Strain"},
{'id': 16, 'name': u"The Vampire Diaries"},
{'id': 17, 'name': u"Under the Dome"},
{'id': 18, 'name': u"Walking Dead"}]

exposed = True

@cherrypy.tools.json_out()
def GET(self):
return self._torrents

def DELETE(self, id):
for torrent in self._torrents:
if torrent.get('id') == int(id):
self._torrents.remove(torrent)
break


if __name__ == '__main__':
conf = {
'/': {
'tools.staticdir.root': os.path.abspath(os.getcwd()),
},
'/static': {
'tools.staticdir.on': True,
'tools.staticdir.dir': './static'
},
'/api/torrents': {
'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
}
}

cherrypy.quickstart(App(), config=conf)
23 changes: 23 additions & 0 deletions static/add-torrent-dialog.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<md-dialog style="width: 500px;" aria-label="Add Torrent">
<md-toolbar>
<div class="md-toolbar-tools">
<h2>Add Torrent</h2>
</div>
</md-toolbar>
<md-dialog-content>
<md-input-container>
<label>URL</label>
<input type="url" ng-change="parseUrl()" ng-model="url" ng-model-options="{ debounce: 500 }">
</md-input-container>
<md-progress-linear md-mode="indeterminate" ng-show="isloading"></md-progress-linear>
<span ng-show="isloaded" style="text-align: center">{{title}}</span>
</md-dialog-content>
<div class="md-actions" layout="row">
<md-button ng-click="cancel()">
Cancel
</md-button>
<md-button ng-click="add()" class="md-primary" ng-disabled="disabled">
Add
</md-button>
</div>
</md-dialog>
102 changes: 102 additions & 0 deletions static/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
var app = angular.module('monitorrent', ['ngMaterial', 'ngRoute']);

var routes = [
{href: "/torrents", include: 'torrents-partial.html', label: 'Torrents', controller: 'TorrentsController'},
{href: "/clients", include: 'settings-partial.html', label: 'Clients', controller: 'SettingsController'},
{href: "/settings", include: 'settings-partial.html', label: 'Settings', controller: 'SettingsController'},
{href: "/logs", include: 'settings-partial.html', label: 'Logs', controller: 'SettingsController'},
{href: "/execute", include: 'settings-partial.html', label: 'Execute', controller: 'SettingsController'},
{href: "/about", include: 'settings-partial.html', label: 'About', controller: 'SettingsController'}
];

app.config(function ($routeProvider) {
for (var i = 0; i < routes.length; i++) {
route = routes[i];
$routeProvider.when(route.href, {
templateUrl: route.include,
controller: route.controller
});
}
$routeProvider.otherwise(routes[0].href)
});

app.controller('AppCtrl', function ($scope) {
$scope.routes = routes;
});

app.controller('TorrentsController', function ($scope, TorrentsService, $mdDialog, $log) {
function updateTorrents() {
TorrentsService.all().success(function (data) {
$scope.torrents = data
});
}

function AddTorrentDialogController($scope, $mdDialog) {
$scope.cancel = function() {
$mdDialog.cancel();
};
$scope.add = function() {
$mdDialog.hide();
};
$scope.parseUrl = function () {
$scope.isloading = true;
$scope.isloaded = false;
$scope.disabled = true;
TorrentsService.parseUrl($scope.url).success(function (data) {
$scope.title = data;
$scope.isloading = false;
$scope.isloaded = true;
$scope.disabled = false;
}).error(function () {
$scope.title = "Error";
$scope.isloading = false;
$scope.isloaded = true;
$scope.disabled = true;
});
};
$scope.url = "";
$scope.disabled = true;
$scope.isloading = false;
$scope.isloaded = false;
}

$scope.deleteTorrent = function (id) {
TorrentsService.delete(id).success(function (data) {
updateTorrents();
});
};

$scope.showAddTorrentDialog = function (ev) {
$mdDialog.show({
controller: AddTorrentDialogController,
templateUrl: 'add-torrent-dialog.html',
parent: angular.element(document.body),
targetEvent: ev
}).then(function() {
$log.info("Add Torrent");
}, function() {
$log.info("Cancel add torrent");
})
};

updateTorrents();
});

app.controller('SettingsController', function ($scope) {
});

app.factory('TorrentsService', function ($http) {
torrentsService = {
all: function () {
return $http.get("/api/torrents");
},
delete: function (id) {
return $http.delete("/api/torrents/" + id);
},
parseUrl: function(url) {
return $http.get("/api/parse", {params: {url: url}});
}
};

return torrentsService;
});
41 changes: 41 additions & 0 deletions static/index5.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en" ng-app="monitorrent">
<head>
<title>Monitorrent</title>

<link rel="stylesheet"
href="https://ajax.googleapis.com/ajax/libs/angular_material/0.10.0/angular-material.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=RobotoDraft:300,400,500,700,400italic">
<link rel="stylesheet" href="monitorrent.css">
<meta name="viewport" content="initial-scale=1"/>

</head>
<body class="monitorrent" layout="column" ng-controller="AppCtrl">
<md-toolbar layout="row" class="md-whiteframe-z3">
<div class="md-toolbar-tools">
<h1>Monitorrent</h1>
</div>
</md-toolbar>

<div layout="row" flex>
<md-sidenav class="site-sidenav md-sidenav-left md-whiteframe-z2" md-is-locked-open="$mdMedia('gt-sm')">
<md-content>
<md-button ng-repeat="route in routes" href="#{{route.href}}">{{route.label}}</md-button>
</md-content>
</md-sidenav>

<div ng-view flex>
</div>
</div>

<!-- Angular Material Dependencies -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-route.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-animate.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-aria.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/0.9.4/angular-material.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-material-icons/0.5.0/angular-material-icons.min.js"></script>

<script src="app.js"></script>
</body>
</html>
11 changes: 11 additions & 0 deletions static/monitorrent.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
html, body {
/*font-family: 'Roboto', 'Helvetica', sans-serif;*/
}

.monitorrent .site-sidenav .md-button {
width: 100%;
margin-left: 0;
margin-right: 0;
text-align: left;
padding-left: 16px;
}
Loading

0 comments on commit 1e163c6

Please sign in to comment.