├── .gitignore
├── public
├── config.inc.php.template
├── style.css
├── query.php
├── index.php
├── functions.inc.php
└── app.js
├── LICENSE
├── README.md
└── tvshowmanager.sql
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | config.inc.php
3 |
--------------------------------------------------------------------------------
/public/config.inc.php.template:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/public/style.css:
--------------------------------------------------------------------------------
1 | .navbar-right { margin-right: 0; }
2 |
3 |
4 | /** Animation **/
5 | .gly-spin {
6 | -webkit-animation: spin 2s infinite linear;
7 | -moz-animation: spin 2s infinite linear;
8 | -o-animation: spin 2s infinite linear;
9 | animation: spin 2s infinite linear;
10 | }
11 | @-moz-keyframes spin {
12 | 0% {
13 | -moz-transform: rotate(0deg);
14 | }
15 | 100% {
16 | -moz-transform: rotate(359deg);
17 | }
18 | }
19 | @-webkit-keyframes spin {
20 | 0% {
21 | -webkit-transform: rotate(0deg);
22 | }
23 | 100% {
24 | -webkit-transform: rotate(359deg);
25 | }
26 | }
27 | @-o-keyframes spin {
28 | 0% {
29 | -o-transform: rotate(0deg);
30 | }
31 | 100% {
32 | -o-transform: rotate(359deg);
33 | }
34 | }
35 | @keyframes spin {
36 | 0% {
37 | -webkit-transform: rotate(0deg);
38 | transform: rotate(0deg);
39 | }
40 | 100% {
41 | -webkit-transform: rotate(359deg);
42 | transform: rotate(359deg);
43 | }
44 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 WaeCo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Tv Show Manager
2 | ===============
3 |
4 | **This Project is currently porting to Node.JS.** Please have a look at [node](https://github.com/WaeCo/TVShowManager/tree/node) branch.
5 |
6 | This is a nice and simple Web UI that allows you to keep track of your TV shows.
7 | It is useful if you are watching multiple shows at the same time and cannot remember at which episode you stopped watching.
8 | Also, it shows you if a new episode is available or when the next comes online.
9 |
10 | 
11 |
12 |
13 |
14 | ## Usage
15 | You can either use my site at https://www.waeco-soft.com/TvShowManager or setup your own homepage.
16 |
17 | When using my site you can try it out without creating an account. But keep in mind your shows won't be saved unless you create an account.
18 |
19 | ## Installation
20 | Clone this repository
21 |
22 | #### Database
23 | 1. Install a MySQL server
24 | 2. Create a database `tvshowmanager`
25 | 3. Create a user `tvshowmanager` and set a password
26 | 4. Import `tvshowmanager.sql` into that database
27 |
28 | #### Webserver
29 | 1. Install a Webserver and setup PHP
30 | 2. Copy all files from `public` into a directory in your webroot
31 |
32 | #### Config
33 | 1. Get a API key from http://thetvdb.com/?tab=apiregister
34 | 2. Rename `config.inc.php.template` to `config.inc.php`
35 | 3. Modify the settings to match your MySQL server and your API key
36 |
--------------------------------------------------------------------------------
/public/query.php:
--------------------------------------------------------------------------------
1 | error);
8 |
9 | $user = 0;
10 | if(isset($_SESSION['userid']))
11 | $user = $_SESSION['userid'];
12 | elseif(isset($_COOKIE['usertoken'])) {
13 | $res = login2($_COOKIE['usertoken']);
14 | if($res['msg'] == 'OK')
15 | $user = $_SESSION['userid'];
16 | }
17 |
18 | $postdata = file_get_contents("php://input");
19 | if(!empty($postdata))
20 | $_POST = json_decode($postdata,TRUE);
21 |
22 | if(!empty($_GET['search']))
23 | $res = search(urldecode($_GET['search']));
24 |
25 | elseif(!empty($_GET['show']))
26 | $res = getShow($_GET['show'], isset($_GET['force']), isset($_GET['q']));
27 |
28 | elseif(isset($_GET['usershows']))
29 | $res = getUserShows();
30 |
31 | elseif(!empty($_POST['addshow']))
32 | $res = addShow($_POST['addshow']);
33 |
34 | elseif(!empty($_POST['delshow']))
35 | $res = delShow($_POST['delshow']);
36 |
37 | elseif(!empty($_POST['updateshow']))
38 | $res = updateShow($_POST['updateshow']);
39 |
40 | elseif(!empty($_POST['username']) && !empty($_POST['password']))
41 | $res = login($_POST['username'], $_POST['password'], isset($_POST['stay']) && $_POST['stay']);
42 |
43 | elseif(!empty($_POST['token']))
44 | $res = login2($_POST['password']);
45 |
46 | elseif(isset($_POST['logout']))
47 | $res = logout();
48 |
49 | elseif(!empty($_POST['registername']) && !empty($_POST['password']))
50 | $res = register($_POST['registername'], $_POST['password']);
51 |
52 | else {
53 | $res = array('msg' => 'Command not set', 'post' => $_POST, 'get' => $_GET);
54 | }
55 |
56 | header("Content-type: application/json");
57 | echo json_encode($res);
58 |
59 | ?>
60 |
--------------------------------------------------------------------------------
/tvshowmanager.sql:
--------------------------------------------------------------------------------
1 | SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
2 | SET time_zone = "+00:00";
3 |
4 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
5 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
6 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
7 | /*!40101 SET NAMES utf8 */;
8 |
9 |
10 | CREATE TABLE IF NOT EXISTS `episode` (
11 | `show_id` int(11) NOT NULL,
12 | `season` smallint(6) NOT NULL,
13 | `episode` smallint(6) NOT NULL,
14 | `title` text NOT NULL,
15 | `airdate` date NOT NULL
16 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
17 |
18 | CREATE TABLE IF NOT EXISTS `show` (
19 | `show_id` int(11) NOT NULL,
20 | `imdb_id` varchar(12) NOT NULL,
21 | `name` varchar(255) NOT NULL,
22 | `started` date NOT NULL,
23 | `ended` date NOT NULL,
24 | `air_day` varchar(12) NOT NULL,
25 | `air_time` varchar(12) NOT NULL,
26 | `status` varchar(32) NOT NULL,
27 | `image` text NOT NULL,
28 | `seasons` int(11) NOT NULL,
29 | `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
30 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
31 |
32 | CREATE TABLE IF NOT EXISTS `user` (
33 | `user_id` int(11) NOT NULL,
34 | `name` varchar(64) NOT NULL,
35 | `password` varchar(64) NOT NULL
36 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
37 |
38 | CREATE TABLE IF NOT EXISTS `user_shows` (
39 | `user_id` int(11) NOT NULL,
40 | `show_id` int(11) NOT NULL,
41 | `last_season` int(11) NOT NULL DEFAULT '0',
42 | `last_episode` int(11) NOT NULL DEFAULT '0',
43 | `enabled` tinyint(1) NOT NULL DEFAULT '1',
44 | `favourite` tinyint(1) NOT NULL DEFAULT '0'
45 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
46 |
47 | CREATE TABLE IF NOT EXISTS `user_token` (
48 | `user_id` int(11) NOT NULL,
49 | `token` varchar(32) NOT NULL
50 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
51 |
52 |
53 | ALTER TABLE `episode`
54 | ADD PRIMARY KEY (`show_id`,`episode`,`season`);
55 |
56 | ALTER TABLE `show`
57 | ADD PRIMARY KEY (`show_id`),
58 | ADD UNIQUE KEY `imdb_id` (`imdb_id`),
59 | ADD KEY `name` (`name`);
60 |
61 | ALTER TABLE `user`
62 | ADD PRIMARY KEY (`user_id`),
63 | ADD UNIQUE KEY `name` (`name`);
64 |
65 | ALTER TABLE `user_shows`
66 | ADD PRIMARY KEY (`user_id`,`show_id`) USING BTREE,
67 | ADD KEY `show_id` (`show_id`);
68 |
69 | ALTER TABLE `user_token`
70 | ADD PRIMARY KEY (`user_id`,`token`);
71 |
72 |
73 | ALTER TABLE `user`
74 | MODIFY `user_id` int(11) NOT NULL AUTO_INCREMENT;
75 |
76 | ALTER TABLE `user_shows`
77 | ADD CONSTRAINT `user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE;
78 |
79 | ALTER TABLE `user_token`
80 | ADD CONSTRAINT `user_token_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE;
81 |
82 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
83 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
84 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
85 |
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | error);
10 |
11 | $user = 0;
12 | $name = '';
13 | if(isset($_SESSION['userid'])) {
14 | $user = $_SESSION['userid'];
15 | $name = $_SESSION['username'];
16 | } elseif(isset($_COOKIE['usertoken'])) {
17 | $res = login2($_COOKIE['usertoken']);
18 | if($res['msg'] == 'OK') {
19 | $user = $_SESSION['userid'];
20 | $name = $_SESSION['username'];
21 | }
22 | }
23 |
24 | ?>
25 |
26 |
27 |
28 | TV Show Manager
29 |
30 |
31 |
32 |
33 |
34 |
35 |
43 |
44 |
45 |
46 |
47 |
95 |
96 |
97 | '.$res['msg'].'
'; ?>
98 |
×Added {{last_added_show}} to your list
99 |
100 |
125 |
126 |
127 |
128 |
129 | |
130 |
131 |
132 |
133 | |
134 |
135 | Name
136 |
137 | |
138 | Next Episode |
139 |
140 | Status
141 |
142 | |
143 | Episode Name |
144 | Date |
145 | |
146 |
147 |
148 | |
149 |
150 | |
151 | {{show.name}} |
152 |
153 | {{show.next_ep}}
154 |
155 |
156 | |
157 | {{show.status}} |
158 | {{show.next_ep_name}} |
159 | {{show.next_ep_date}} |
160 |
161 |
162 |
163 |
164 |
165 | |
166 |
167 |
168 |
169 |
170 |
171 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
--------------------------------------------------------------------------------
/public/functions.inc.php:
--------------------------------------------------------------------------------
1 | 2);
17 | if($data === FALSE) {
18 | # http_response_code(404);
19 | # echo "Nope";
20 | return FALSE;
21 | }
22 |
23 | $json = substr($data, 5+strlen($str)+2, -1);
24 | $obj = json_decode($json,TRUE);
25 |
26 | #print_r($obj);
27 |
28 | $res = array('show' => array());
29 | if(isset($obj['d'])) {
30 | foreach ($obj['d'] as $show) {
31 | #echo strtolower($show['l'])."\n";
32 | if(isset($show['q']) && $show['q'] == "TV series" && startsWith(strtolower($show['l']), strtolower($title))) {
33 | $res['show'][] = array(
34 | 'imdb_id' => $show['id'],
35 | 'name' => $show['l'],
36 | 'year' => $show['y'],
37 | 'img' => isset($show['i']) ? $show['i'][0] : ''
38 | );
39 | }
40 | }
41 | }
42 | return $res;
43 | }
44 |
45 | //http://thetvdb.com/wiki/index.php?title=Programmers_API
46 | function getShow($sid, $force, $quiet) {
47 | global $db, $user, $apikey;
48 |
49 | $stm = $db->prepare('
50 | SELECT s.*, us.last_season, us.last_episode, us.enabled, us.favourite
51 | FROM `show` as s
52 | LEFT JOIN user_shows as us
53 | ON s.show_id = us.show_id AND us.user_id = ?
54 | WHERE s.show_id = ?
55 | ');
56 | $stmEpisodes = $db->prepare('SELECT season,episode,title,airdate FROM episode WHERE show_id = ?');
57 |
58 | if(!$force) {
59 | $res = doOutput($stm, $stmEpisodes, $sid, $user, 'local', $quiet);
60 | if($res)
61 | return $res;
62 | }
63 |
64 | $data = @file_get_contents("http://thetvdb.com/api/$apikey/series/$sid/all");
65 | if($data === FALSE) {
66 | http_response_code(502);
67 | return "API Error";
68 | }
69 |
70 | $xml = simplexml_load_string($data);
71 | $json = json_encode($xml);
72 | $obj = json_decode($json,TRUE);
73 |
74 | if(!isset($obj['Series'])) {
75 | http_response_code(404);
76 | return "Show '$sid' not found";
77 | }
78 |
79 | #print_r($obj);
80 | $series = $obj['Series'];
81 | $episodes = $obj['Episode'];
82 |
83 | #Inster Show
84 | $stmShow = $db->prepare('REPLACE INTO `show` (show_id, imdb_id, name, started, air_day, air_time, status, image, updated_at) VALUES(?, ?, ?, ?, ?, ?, ?, ?, NOW())') or die($db->error);
85 | $stmEp = $db->prepare('INSERT INTO episode (show_id, season, episode, title, airdate) VALUES(?, ?, ?, ?, ?)') or die($db->error);
86 |
87 | $db->query("DELETE FROM episode WHERE show_id = $sid") or die($db->error);
88 |
89 | $img = empty($series['poster']) ? '' : 'http://thetvdb.com/banners/_cache/'.$series['poster'];
90 | $stmShow->bind_param('isssssss', $series['id'], $series['IMDB_ID'], $series['SeriesName'], $series['FirstAired'], $series['Airs_DayOfWeek'], $series['Airs_Time'], $series['Status'], $img);
91 | $stmShow->execute() or die($db->error);
92 |
93 | #Inster Episodes
94 | if(isset($episodes['EpisodeName'])) {
95 | $episode = $episodes;
96 | $episode['FirstAired'] = empty($episode['FirstAired']) ? '0000-00-00' : $episode['FirstAired'];
97 | $episode['EpisodeName'] = empty($episode['EpisodeName']) ? 'TBA' : $episode['EpisodeName'];
98 | $stmEp->bind_param('iiiss', $sid, $episode['SeasonNumber'], $episode['EpisodeNumber'], $episode['EpisodeName'], $episode['FirstAired']);
99 | $stmEp->execute();
100 | } else {
101 | foreach($episodes as $episode) {
102 | $episode['FirstAired'] = empty($episode['FirstAired']) ? '0000-00-00' : $episode['FirstAired'];
103 | $episode['EpisodeName'] = empty($episode['EpisodeName']) ? 'TBA' : $episode['EpisodeName'];
104 | $stmEp->bind_param('iiiss', $sid, $episode['SeasonNumber'], $episode['EpisodeNumber'], $episode['EpisodeName'], $episode['FirstAired']);
105 | $stmEp->execute();
106 | }
107 | }
108 |
109 | $res = doOutput($stm, $stmEpisodes, $sid, $user, 'remote', $quiet);
110 | if($res)
111 | return $res;
112 | else {
113 | http_response_code(404);
114 | return "Some Error";
115 | }
116 | }
117 |
118 | function getUserShows() {
119 | global $db, $user;
120 |
121 | $stm = $db->prepare('
122 | SELECT
123 | s.*,
124 | us.last_season, us.last_episode, us.enabled, us.favourite
125 | FROM user_shows as us
126 | JOIN `show` as s
127 | USING(show_id)
128 | WHERE us.user_id = ? AND 0 = ?
129 | ');
130 | $stmEpisodes = $db->prepare('SELECT season,episode,title,airdate FROM episode WHERE show_id = ?');
131 | $res = doOutput($stm, $stmEpisodes, 0, $user, 'local');
132 | if(isset($res['show_id']))
133 | $res = array($res);
134 | return $res;
135 | }
136 |
137 | function doOutput($stm, $stmEpisodes, $sid, $user, $type, $quiet = false) {
138 | $stm->bind_param('ii', $user, $sid);
139 |
140 | if($stm->execute()) {
141 | $res = $stm->get_result();
142 | $result = array();
143 | while($obj = $res->fetch_assoc()) {
144 | $obj['request_type'] = $type;
145 | $obj['seasons'] = array();
146 | if($obj['name']) {
147 | $stmEpisodes->bind_param('i', $obj['show_id']);
148 | if($stmEpisodes->execute()) {
149 | $res2 = $stmEpisodes->get_result();
150 | while ($row = $res2->fetch_assoc()) {
151 | if($row['season'] == 0) continue;
152 | if(isset($obj['seasons'][$row['season']]))
153 | $obj['seasons'][$row['season']][] = $row;
154 | else
155 | $obj['seasons'][$row['season']] = array($row);
156 | }
157 | }
158 | } else
159 | $obj = $obj['show_id'];
160 | if($res->num_rows == 1)
161 | $result = $obj;
162 | else
163 | $result[] = $obj;
164 | }
165 | if($quiet)
166 | return "OK";
167 | else
168 | return $result;
169 | }
170 | return false;
171 | }
172 |
173 |
174 | function addShow($id) {
175 | global $db, $user;
176 | $id = $db->escape_string($id);
177 | $res = $db->query("SELECT show_id FROM `show` WHERE imdb_id = '$id'");
178 | if($res->num_rows == 1) {
179 | $show_id = $res->fetch_assoc()['show_id'];
180 | } else {
181 | $data = @file_get_contents("http://thetvdb.com/api/GetSeriesByRemoteID.php?imdbid=$id");
182 | if($data === FALSE) {
183 | http_response_code(502);
184 | return "API Error";
185 | }
186 |
187 | $xml = simplexml_load_string($data);
188 | $json = json_encode($xml);
189 | $obj = json_decode($json,TRUE);
190 |
191 | if(!isset($obj['Series'])) {
192 | http_response_code(404);
193 | return "Show not found";
194 | }
195 | $show_id = $obj['Series']['seriesid'];
196 | getShow($show_id, TRUE, TRUE);
197 | }
198 | $stm = $db->prepare('INSERT INTO user_shows (user_id, show_id) VALUES(?,?)');
199 | $stm->bind_param('ii', $user, $show_id);
200 | $stm->execute();
201 | return $show_id;
202 | }
203 |
204 | function delShow($id) {
205 | global $db, $user;
206 | $stm = $db->prepare('DELETE FROM user_shows WHERE user_id = ? AND show_id = ?');
207 | $stm->bind_param('ii', $user, $id);
208 | return $stm->execute();
209 | }
210 |
211 | function updateShow($id) {
212 | global $db, $user;
213 | $stm = $db->prepare('UPDATE user_shows SET last_season = ?, last_episode = ?, enabled = ?, favourite = ? WHERE user_id = ? AND show_id = ?');
214 | $stm->bind_param('iiiiii', $_POST['last_season'], $_POST['last_episode'], $_POST['enabled'], $_POST['favourite'], $user, $id);
215 | return $stm->execute();
216 | }
217 |
218 | function login($user, $pw, $stay = false) {
219 | global $db;
220 | $user = $db->escape_string($user);
221 | $pw = $db->escape_string($pw);
222 | $res = $db->query("SELECT user_id,name FROM `user` WHERE `name` = '$user' AND `password` = PASSWORD('$pw')") or die($db->error);
223 | if($res->num_rows == 1) {
224 | $row = $res->fetch_assoc();
225 | $id = $row['user_id'];
226 | $_SESSION['userid'] = $id;
227 | $_SESSION['username'] = $row['name'];
228 | if($stay) {
229 | $token = md5("$id ## $user " + rand() + date('c'));
230 | $db->query("INSERT INTO `user_token` VALUES ($id, '$token')") or die($db->error);
231 | setcookie("usertoken", $token, time()+60*60*24*30, "", "", false, true);
232 | } else {
233 | setcookie("usertoken", NULL);
234 | }
235 |
236 | return array('msg' => 'OK');
237 | } else
238 | return array('msg' => 'Username or Password invalid');
239 | }
240 | function login2($usertoken) {
241 | global $db;
242 | $token = $db->escape_string($usertoken);
243 | $res = $db->query("SELECT user_id, name FROM `user` JOIN `user_token` USING(user_id) WHERE `token` = '$token'") or die($db->error);
244 | if($res->num_rows == 1) {
245 | $row = $res->fetch_assoc();
246 | $_SESSION['userid'] = $row['user_id'];
247 | $_SESSION['username'] = $row['name'];
248 | setcookie("usertoken", $usertoken, time()+60*60*24*30, "", "", false, true);
249 | return array('msg' => 'OK');
250 | } else
251 | return array('msg' => 'Session expiered');
252 | }
253 |
254 | function logout() {
255 | global $db;
256 | $id = $_SESSION['userid'];
257 | if($id)
258 | $db->query("DELETE FROM `user_token` WHERE `user_id` = $id");
259 | unset($_SESSION['userid']);
260 | unset($_SESSION['username']);
261 | setcookie("usertoken", NULL);
262 | }
263 |
264 | function register($user, $pw) {
265 | global $db;
266 | $stm = $db->prepare('INSERT INTO user (name, password) VALUES(?,PASSWORD(?))');
267 | $stm->bind_param('ss', $user, $pw);
268 | if($stm->execute())
269 | echo json_encode(array('msg' => 'OK'));
270 | else
271 | echo json_encode(array('msg' => 'Username already exists'));
272 | exit;
273 | }
274 |
275 | function startsWith($haystack, $needle) {
276 | // search backwards starting from haystack length characters from the end
277 | return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== false;
278 | }
279 |
--------------------------------------------------------------------------------
/public/app.js:
--------------------------------------------------------------------------------
1 | var app = angular.module('TVShowManager', ['ngRoute', 'ui.bootstrap']);
2 | app.config(['$compileProvider', function ($compileProvider) {
3 | $compileProvider.debugInfoEnabled(true);
4 | }]);
5 |
6 | app.factory('ShowQuery', ['$http', '$timeout', function($http, $timeout) {
7 | return new function() {
8 | this.search = function(name) {
9 | return $http.get("query.php", {params: {search: name}});
10 | };
11 | this.showinfo = function(id, force) {
12 | if(force)
13 | return $http.get("query.php", {params: {show: id, force: true}});
14 | else
15 | return $http.get("query.php", {params: {show: id}});
16 | };
17 |
18 | this.user_add_show = function(id) {
19 | return $http.post("query.php", {addshow: id});
20 | };
21 |
22 | this.user_del_show = function(show) {
23 | return $http.post("query.php", {delshow: show.id});
24 | };
25 |
26 | this.user_del_show = function(show) {
27 | return $http.post("query.php", {delshow: show.id});
28 | };
29 |
30 | this.login = function(user, pw, stay) {
31 | return $http.post("query.php", {username: user, password: pw, stay: !!stay});
32 | };
33 |
34 | this.login_token = function(user, token) {
35 | return $http.post("query.php", {username: user, token: token});
36 | };
37 |
38 | this.logout = function() {
39 | return $http.post("query.php", {logout: true});
40 | };
41 |
42 | this.register = function(user, pw) {
43 | return $http.post("query.php", {registername: user, password: pw});
44 | };
45 |
46 | var timer;
47 | this.user_show_update = function(show) {
48 | if(timer)
49 | $timeout.cancel(timer);
50 | timer = $timeout(function(){
51 | $http.post("query.php", {
52 | updateshow: show.id,
53 | last_season: show.last_season,
54 | last_episode: show.last_episode,
55 | enabled: !show.done,
56 | favourite: show.favourite
57 | }, {headers : {'Content-Type': 'application/x-www-form-urlencoded'}});
58 | }, 500);
59 | };
60 | };
61 | }]);
62 |
63 | app.factory('TVShow', ['ShowQuery', function (ShowQuery) {
64 | return function(id, name) {
65 | this.id = id;
66 | this.name = name || 'Unknown Show';
67 | this.__defineGetter__('class', function() {
68 | switch(this.status) {
69 | case 'Available':
70 | return 'success';
71 | case 'Unavailable':
72 | return 'danger';
73 | case 'Disabled':
74 | case 'Ended':
75 | case 'Canceled':
76 | return 'active';
77 | case 'Undetermined':
78 | case 'Error':
79 | return 'warning';
80 | default:
81 | return '';
82 | };
83 | });
84 | this.status = '?';
85 | this.show_status = '';
86 | this.__defineGetter__('done', function() {
87 | return this.status == 'Disabled' || this.ended;
88 | });
89 | this.__defineGetter__('disabled', function() {
90 | return this.status == 'Disabled';
91 | });
92 | this.__defineGetter__('first', function() {
93 | return this.last_season == 0;
94 | });
95 | this.__defineGetter__('ended', function() {
96 | return this.status == 'Ended' || this.status == 'Canceled';
97 | });
98 | this.__defineGetter__('next_ep', function() {
99 | var next = this.getNext();
100 | if(next)
101 | return 'S' + pad(next.season, 2) + ' E' + pad(next.episode, 2);
102 | if(this.ended)
103 | return 'End';
104 | //Fallback
105 | return 'S' + pad(this.last_season, 2) + ' E' + pad(this.last_episode, 2) + "+";
106 | //return 'Next';
107 | });
108 | this.__defineGetter__('next_ep_name', function() {
109 | var next = this.getNext();
110 | return next ? next.title : this.show_status;
111 | });
112 | this.__defineGetter__('next_ep_date', function() {
113 | var next = this.getNext();
114 | return next ? next.airdate : '';
115 | });
116 |
117 | this.last_season = 0;
118 | this.last_episode = 0;
119 | this.favourite = false;
120 |
121 | this.loading = false;
122 | this.image = '';
123 |
124 | this.seasons = {};
125 |
126 | /* Functions */
127 |
128 | this.inc = function() {
129 | if(this.loading) return;
130 | this.last_episode++;
131 | if(this.last_season == 0 || this.last_episode > this.seasons[this.last_season].length) {
132 | if(this.last_season < this.seasons.length) {
133 | this.last_season++;
134 | this.last_episode = 1;
135 | } else
136 | this.last_episode--;
137 | }
138 | this.update_status();
139 | ShowQuery.user_show_update(this);
140 | };
141 |
142 | this.dec = function() {
143 | if(this.loading) return;
144 | this.last_episode--;
145 | if(this.last_episode == 0) {
146 | this.last_episode = 1;
147 | if(this.last_season > 1) {
148 | this.last_season--;
149 | this.last_episode = this.seasons[this.last_season].length;
150 | } else {
151 | this.last_episode = 0;
152 | this.last_season = 0;
153 | }
154 | }
155 | this.update_status();
156 | ShowQuery.user_show_update(this);
157 | };
158 |
159 | this.activate = function() {
160 | if(this.status != 'Disabled') {
161 | this.status = 'Disabled';
162 | } else
163 | this.update_status();
164 | ShowQuery.user_show_update(this);
165 | console.log(this);
166 | };
167 |
168 | this.setFavourite = function() {
169 | this.favourite = !this.favourite;
170 | ShowQuery.user_show_update(this);
171 | }
172 |
173 | this.getNext = function() {
174 | if(this.last_season > 0 && this.seasons[this.last_season].length > this.last_episode)
175 | return this.seasons[this.last_season][this.last_episode];
176 | if(this.seasons.length >= this.last_season + 1)
177 | return this.seasons[this.last_season + 1][0];
178 | return null;
179 | }
180 |
181 | this.update_status = function(data) {
182 | if(data) {
183 | this.seasons = data.seasons;
184 | var max = 0;
185 | for(var i in data.seasons)
186 | max = parseInt(i) > max ? parseInt(i) : max;
187 | this.seasons.length = max;
188 | this.image = data.image;
189 | this.name = data.name;
190 | this.show_status = data.status == 'Canceled/Ended' ? 'Ended' : data.status;
191 | if((data.enabled != null && !data.enabled) && !(this.show_status == 'Ended' || this.show_status == 'Canceled'))
192 | this.status = 'Disabled';
193 |
194 | this.last_season = data.last_season || 0;
195 | this.last_episode = data.last_episode || 0;
196 | this.favourite = !!data.favourite;
197 |
198 | if(this.status == 'Disabled') return;
199 | }
200 |
201 | if(this.seasons.length == this.last_season && this.last_episode >= this.seasons[this.last_season].length) {
202 | if(this.show_status == 'Ended' || this.show_status == 'Canceled') {
203 | this.status = this.show_status;
204 | } else {
205 | this.status = 'Unavailable';
206 | }
207 | } else if(this.seasons.length >= this.last_season) {
208 | var nextep = {};
209 | if(this.last_season > 0 && this.last_episode < this.seasons[this.last_season].length)
210 | nextep = this.seasons[this.last_season][this.last_episode];
211 | else
212 | nextep = this.seasons[this.last_season+1][0];
213 |
214 | var now = new Date();
215 | var next = nextep.airdate.split('-');
216 | var nextdate = new Date(next[0], next[1]-1, next[2]);
217 | if(nextdate.getTime() < 0) {
218 | this.status = 'Undetermined';
219 | } else if(nextdate <= now) {
220 | this.status = 'Available';
221 | } else {
222 | this.status = 'Unavailable';
223 | }
224 | } else {
225 | this.status = 'Error';
226 | console.error('Last season is greater then total seasons', this);
227 | }
228 |
229 | if(data && (data.enabled != null && !data.enabled) && !(this.status == 'Ended' || this.status == 'Canceled')) {
230 | this.status = 'Disabled';
231 | }
232 | };
233 |
234 | this.refresh = function(force) {
235 | //console.log("Refreshing Show ", this.name, this.id);
236 |
237 | this.loading = true;
238 | var me = this;
239 | ShowQuery.showinfo(this.id, force).
240 | success(function(data, status, headers, config) {
241 | //console.log(data);
242 | me.update_status(data);
243 |
244 | me.loading = false;
245 | }).error(function() {
246 | me.status = 'Error';
247 | me.loading = false;
248 | });
249 | };
250 | if(typeof id == 'object') {
251 | this.id = id.show_id;
252 | this.update_status(id);
253 | } else
254 | this.refresh();
255 |
256 | this.delete = function() {
257 | //console.log("Deleting Show", this.name);
258 | ShowQuery.user_del_show(this);
259 | }
260 | };
261 | }]);
262 |
263 | app.controller('GlobalController', [
264 | '$scope', 'ShowQuery', '$timeout', '$interval', 'TVShow','$uibModal',
265 | function($scope, ShowQuery, $timeout, $interval, TVShow, $modal)
266 | {
267 | $scope.show_predicate = 'favourite';
268 | $scope.show_reverse = true;
269 | $scope.get_show_predicate = function() {
270 | function order_status(elem) {
271 | switch(elem.status) {
272 | case 'Available':
273 | return 100;
274 | case 'Unavailable':
275 | return 90;
276 | case 'Done':
277 | case 'Ended':
278 | case 'Canceled':
279 | return 50;
280 | case 'Error':
281 | return 10;
282 | default:
283 | return 1;
284 | }
285 | };
286 | return [$scope.show_predicate=='status' ? order_status : $scope.show_predicate, '+favourite', '-name', order_status];
287 | };
288 |
289 | $scope.user = {
290 | loggedin: userid ? true : false,
291 | id: userid,
292 | name: username
293 | };
294 |
295 |
296 | $scope.shows = [];
297 | for(var i = 0; i < usershows.length; i++)
298 | $scope.shows.push(new TVShow(usershows[i]));
299 |
300 | var old_search = '';
301 | var timer = 0;
302 | $scope.show_search = function(name) {
303 | $scope.new_error = false;
304 | if(!name || name == '') return;
305 | $scope.search.searching = true;
306 | if(timer){
307 | $timeout.cancel(timer);
308 | }
309 | timer = $timeout(function(){
310 | if(old_search == name)
311 | $scope.search.open = true;
312 | else {
313 | //$scope.search.results = [];
314 | ShowQuery.search(name)
315 | .success(function(data) {
316 | //console.log("Seach open", data);
317 | old_search = name;
318 | if(data)
319 | $scope.search.results = Array.isArray(data.show) ? data.show : [data.show];
320 | else {
321 | $scope.search.results = [];
322 | $scope.search.new_error = true;
323 | }
324 | $scope.search.open = true;
325 | $scope.search.searching=false;
326 | });
327 | }
328 | },500);
329 | };
330 |
331 | $scope.show_add = function(sid, name) {
332 | var id = 0;
333 | if(sid)
334 | id=sid;
335 | else if($scope.search.results && $scope.search.results.length > 0)
336 | id=$scope.search.results[0].imdb_id;
337 |
338 | if(id) {
339 | if(find_array($scope.shows, function(elem) { return elem.id == id; }))
340 | alert("Show is already in list");
341 | else {
342 | ShowQuery.user_add_show(id).
343 | success(function(data,status) {
344 | $scope.shows.push(new TVShow(data, name));
345 | $scope.last_added_show = name;
346 | }).
347 | error(function() {
348 | alert("Something went wrong. Please try again");
349 | });
350 |
351 | $scope.new_name = '';
352 | $scope.search.open = false;
353 | $scope.search.searching=false;
354 | }
355 | } else {
356 | //console.log("No ID set");
357 | $scope.new_error = true;
358 | }
359 | };
360 |
361 | $scope.show_delete = function(show) {
362 | for(var i = 0; i < $scope.shows.length; i++) {
363 | if($scope.shows[i].id == show.id) {
364 | $scope.shows.splice(i,1);
365 | break;
366 | }
367 | }
368 | show.delete();
369 | };
370 |
371 | var inc_interval, inc_timeout;
372 | document.querySelector("body").
373 | addEventListener("mouseup", function() {
374 | if(inc_timeout) {
375 | $timeout.cancel(inc_timeout);
376 | inc_timeout = null;
377 | }
378 | if(inc_interval) {
379 | $interval.cancel(inc_interval);
380 | inc_interval = null;
381 | }
382 | });
383 | $scope.show_start_inc = function(show) {
384 | show.inc();
385 | if(inc_timeout)
386 | $timeout.cancel(inc_timeout);
387 | inc_timeout = $timeout(function(){
388 | if(inc_interval)
389 | $interval.cancel(inc_interval);
390 | inc_interval = $interval(function() {
391 | show.inc();
392 | }, 100);
393 | }, 250);
394 | }
395 |
396 | $scope.show_start_dec = function(show) {
397 | show.dec();
398 | if(inc_timeout)
399 | $timeout.cancel(inc_timeout);
400 | inc_timeout = $timeout(function(){
401 | if(inc_interval)
402 | $interval.cancel(inc_interval);
403 | inc_interval = $interval(function() {
404 | show.dec();
405 | }, 100);
406 | }, 250);
407 | }
408 |
409 | $scope.openLogin = function() {
410 | $modal.open({
411 | templateUrl: 'loginModal.html',
412 | controller: 'LoginModal'
413 | });
414 | };
415 |
416 | $scope.user_logout = function() {
417 | ShowQuery.logout().
418 | success(function(data) {
419 | window.location.reload();
420 | });
421 | };
422 | }]);
423 |
424 | app.controller('LoginModal', [
425 | '$scope', 'ShowQuery', '$uibModalInstance',
426 | function($scope, ShowQuery, $modalInstance)
427 | {
428 | $scope.error = null;
429 | $scope.success = null;
430 |
431 | $scope.closeLogin = function(a1) {
432 | $modalInstance.dismiss('close');
433 | };
434 |
435 | $scope.user_login = function(user, pw, stay) {
436 | $scope.error = null;
437 | $scope.success = null;
438 |
439 | ShowQuery.login(user, pw, stay).
440 | success(function(data) {
441 | //console.log("Login: ",data);
442 | if(data.msg == "OK") {
443 | $scope.success = "Login Successful";
444 | window.location.reload();
445 | } else {
446 | $scope.error = data.msg;
447 | }
448 | });
449 | return false;
450 | };
451 |
452 | $scope.user_register = function(user, pw) {
453 | $scope.error = null;
454 | $scope.success = null;
455 |
456 | ShowQuery.register(user, pw).
457 | success(function(data) {
458 | //console.log("Register: ",data);
459 | if(data.msg == "OK") {
460 | $scope.success = "Registration Successful. You can login now";
461 | } else {
462 | $scope.error = data.msg;
463 | }
464 | });
465 | return false;
466 | };
467 | }]);
468 |
469 | function find_array(array, compare) {
470 | if(!Array.isArray(array)) return false;
471 | for(var i = 0; i < array.length; i++)
472 | if(compare(array[i])) return true;
473 | return false;
474 | }
475 | function pad(n, width, z) {
476 | z = z || '0';
477 | n = n + '';
478 | return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
479 | }
480 |
--------------------------------------------------------------------------------