Moved eveything related to the api to a seperate folder seasoned_api.
This commit is contained in:
20
seasoned_api/package.json
Normal file
20
seasoned_api/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "seasoned-api",
|
||||
"main": "webserver/server.js",
|
||||
"scripts": {
|
||||
"start": "cross-env SEASONED_CONFIG=../conf/development.json NODE_PATH=. node src/webserver/server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"body-parser": "~1.0.1",
|
||||
"cross-env": "^3.1.3",
|
||||
"express": "~4.0.0",
|
||||
"mongoose": "^3.6.13",
|
||||
"moviedb": "^0.2.7",
|
||||
"node-cache": "^4.1.1",
|
||||
"nodemailer": "^4.0.1",
|
||||
"python-shell": "^0.4.0",
|
||||
"request": "^2.81.0",
|
||||
"request-promise": "^4.2",
|
||||
"sqlite3": "^2.5.0"
|
||||
}
|
||||
}
|
||||
38
seasoned_api/src/config/configuration.js
Normal file
38
seasoned_api/src/config/configuration.js
Normal file
@@ -0,0 +1,38 @@
|
||||
const path = require('path');
|
||||
const Field = require('./field.js');
|
||||
|
||||
let instance = null;
|
||||
|
||||
class Config {
|
||||
constructor() {
|
||||
this.location = Config.determineLocation();
|
||||
this.fields = require(`${this.location}`);
|
||||
}
|
||||
|
||||
static getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new Config();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
static determineLocation() {
|
||||
return path.join(__dirname, '..', '..', process.env.SEASONED_CONFIG);
|
||||
}
|
||||
|
||||
get(section, option) {
|
||||
if (this.fields[section] === undefined || this.fields[section][option] === undefined) {
|
||||
throw new Error(`Filed "${section} => ${option}" does not exist.`);
|
||||
}
|
||||
|
||||
const field = new Field(this.fields[section][option])
|
||||
|
||||
if (field.value === undefined) {
|
||||
throw new Error(`${section} => ${option} is empty.`);
|
||||
}
|
||||
|
||||
return field.value;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Config;
|
||||
16
seasoned_api/src/config/environmentVariables.js
Normal file
16
seasoned_api/src/config/environmentVariables.js
Normal file
@@ -0,0 +1,16 @@
|
||||
class EnvironmentVariables {
|
||||
|
||||
constructor(variables) {
|
||||
this.variables = variables || process.env;
|
||||
}
|
||||
|
||||
get(variable) {
|
||||
return this.variables[variable];
|
||||
}
|
||||
|
||||
has(variable) {
|
||||
return this.get(variable) !== undefined;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EnvironmentVariables;
|
||||
50
seasoned_api/src/config/field.js
Normal file
50
seasoned_api/src/config/field.js
Normal file
@@ -0,0 +1,50 @@
|
||||
const Filters = require('./filters.js');
|
||||
const EnvironmentVariables = require('./environmentVariables.js');
|
||||
|
||||
class Field {
|
||||
|
||||
constructor(rawValue, environmentVariables) {
|
||||
this.rawValue, rawValue;
|
||||
this.filters = new Filters(rawValue);
|
||||
this.valueWithoutFilters = this.filters.removeFiltersFromValue();
|
||||
this.environmentVariables = new EnvironmentVariables(environmentVariables);
|
||||
}
|
||||
|
||||
get value() {
|
||||
if (this.filters.isEmpty()) {
|
||||
return this.valueWithoutFilters;
|
||||
}
|
||||
|
||||
if (this.filters.has('base64') && !this.filters.has('env')) {
|
||||
return Field.base64Decode(this.valueWithoutFilters);
|
||||
}
|
||||
|
||||
if (this.environmentVariables.has(this.valueWithoutFilters) &&
|
||||
this.environmentVariables.get(this.valueWithoutFilters) === '') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!this.filters.has('base64') && this.filters.has('env')) {
|
||||
if (this.environmentVariables.has(this.valueWithoutFilters)) {
|
||||
return this.environmentVariables.get(this.valueWithoutFilters);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (this.filters.has('env') && this.filters.has('base64')) {
|
||||
if (this.environmentVariables.has(this.valueWithoutFilters)) {
|
||||
const encodedEnvironmentVariable = this.environmentVariables.get(this.valueWithoutFilters);
|
||||
return Field.base64Decode(encodedEnvironmentVariable);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.valueWithoutFilters;
|
||||
}
|
||||
|
||||
static base64Decode(string) {
|
||||
return new Buffer(string, 'base64').toString('utf-8');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Field;
|
||||
35
seasoned_api/src/config/filters.js
Normal file
35
seasoned_api/src/config/filters.js
Normal file
@@ -0,0 +1,35 @@
|
||||
class Filters {
|
||||
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
this.delimiter = '|';
|
||||
}
|
||||
|
||||
get filters() {
|
||||
return this.value.split(this.delimiter).slice(0, -1);
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
return !this.hasValidType() || this.filters.length === 0;
|
||||
}
|
||||
|
||||
has(filter) {
|
||||
return this.filters.includes(filter);
|
||||
}
|
||||
|
||||
hasValidType() {
|
||||
return (typeof this.value === 'string');
|
||||
}
|
||||
|
||||
removeFiltersFromValue() {
|
||||
if (this.hasValidType() === false) {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
let filtersCombined = this.filters.join(this.delimiter);
|
||||
filtersCombined += this.filters.length >= 1 ? this.delimiter : '';
|
||||
return this.value.replace(filtersCombined, '');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Filters;
|
||||
15
seasoned_api/src/database/database.js
Normal file
15
seasoned_api/src/database/database.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const configuration = require('src/config/configuration').getInstance();
|
||||
const SqliteDatabase = require('src/database/sqliteDatabase');
|
||||
const database = new SqliteDatabase(configuration.get('database', 'host'));
|
||||
|
||||
/**
|
||||
* This module establishes a connection to the database
|
||||
* specified in the confgiuration file. It tries to setup
|
||||
* the required tables after successfully connecting.
|
||||
* If the tables already exists, it simply proceeds.
|
||||
*/
|
||||
Promise.resolve()
|
||||
.then(() => database.connect())
|
||||
// .then(() => database.setUp());
|
||||
|
||||
module.exports = database;
|
||||
32
seasoned_api/src/database/sqliteDatabase.js
Normal file
32
seasoned_api/src/database/sqliteDatabase.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const sqlite3 = require('sqlite3');
|
||||
|
||||
class SqliteDatabase {
|
||||
|
||||
constructor(host) {
|
||||
this.host = host;
|
||||
this.connection = sqlite3;
|
||||
|
||||
// this.schemaDirectory = path.join(__dirname, 'schemas');
|
||||
}
|
||||
|
||||
connect() {
|
||||
return Promise.resolve()
|
||||
.then(() => new sqlite3.Database(this.host))
|
||||
}
|
||||
|
||||
all(sql, parameters) {
|
||||
return this.connection.all(sql, parameters);
|
||||
}
|
||||
|
||||
get(sql, parameters) {
|
||||
return this.connection.get(sql, parameters);
|
||||
}
|
||||
|
||||
run(sql, parameters) {
|
||||
return this.connection.run(sql, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SqliteDatabase;
|
||||
10
seasoned_api/src/git/gitRepository.js
Normal file
10
seasoned_api/src/git/gitRepository.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const assert = require('assert');
|
||||
|
||||
class GitRepository {
|
||||
|
||||
dumpHook(body) {
|
||||
console.log(body);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GitRepository;
|
||||
13
seasoned_api/src/media_classes/mediaInfo.js
Normal file
13
seasoned_api/src/media_classes/mediaInfo.js
Normal file
@@ -0,0 +1,13 @@
|
||||
class MediaInfo {
|
||||
constructor(device, platform) {
|
||||
this.device = undefined;
|
||||
this.platform = undefined;
|
||||
this.ip = undefined;
|
||||
this.product = undefined;
|
||||
this.title = undefined;
|
||||
this.state = undefined;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MediaInfo;
|
||||
18
seasoned_api/src/media_classes/movie.js
Normal file
18
seasoned_api/src/media_classes/movie.js
Normal file
@@ -0,0 +1,18 @@
|
||||
class Movie {
|
||||
constructor(title, year) {
|
||||
this.id = undefined;
|
||||
this.title = title;
|
||||
this.year = year;
|
||||
this.release_date = undefined;
|
||||
this.library = undefined;
|
||||
this.type = undefined;
|
||||
this.poster = undefined;
|
||||
this.background = undefined;
|
||||
this.matchedInPlex = false;
|
||||
this.childTitle = undefined;
|
||||
this.season = undefined;
|
||||
this.episode = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Movie;
|
||||
13
seasoned_api/src/media_classes/player.js
Normal file
13
seasoned_api/src/media_classes/player.js
Normal file
@@ -0,0 +1,13 @@
|
||||
class Player {
|
||||
constructor(device, address) {
|
||||
this.device = device;
|
||||
this.ip = address;
|
||||
this.platform = undefined;
|
||||
this.product = undefined;
|
||||
this.title = undefined;
|
||||
this.state = undefined;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Player;
|
||||
8
seasoned_api/src/media_classes/user.js
Normal file
8
seasoned_api/src/media_classes/user.js
Normal file
@@ -0,0 +1,8 @@
|
||||
class User {
|
||||
constructor(id, title) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = User;
|
||||
22
seasoned_api/src/plex/convertPlexToMovie.js
Normal file
22
seasoned_api/src/plex/convertPlexToMovie.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const Movie = require('src/media_classes/movie');
|
||||
|
||||
function convertPlexToMovie(plexMovie) {
|
||||
const movie = new Movie();
|
||||
movie.title = plexMovie.title;
|
||||
|
||||
if (plexMovie.type === 'episode') {
|
||||
movie.title = plexMovie.grandparentTitle;
|
||||
movie.childTitle = plexMovie.title;
|
||||
movie.season = plexMovie.parentIndex;
|
||||
movie.episode = plexMovie.index;
|
||||
}
|
||||
movie.year = plexMovie.year;
|
||||
movie.library = plexMovie.librarySectionTitle;
|
||||
movie.type = plexMovie.type;
|
||||
movie.poster = plexMovie.thumb;
|
||||
movie.background = plexMovie.art;
|
||||
|
||||
return movie;
|
||||
}
|
||||
|
||||
module.exports = convertPlexToMovie;
|
||||
17
seasoned_api/src/plex/convertPlexToStream.js
Normal file
17
seasoned_api/src/plex/convertPlexToStream.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const convertPlexToMovie = require('src/plex/convertPlexToMovie');
|
||||
const convertStreamToMediaInfo = require('src/plex/convertStreamToMediaInfo');
|
||||
const convertStreamToPlayer = require('src/plex/stream/convertStreamToPlayer');
|
||||
const convertStreamToUser = require('src/plex/stream/convertStreamToUser');
|
||||
const ConvertStreamToPlayback = require('src/plex/stream/convertStreamToPlayback');
|
||||
|
||||
function convertPlexToStream(plexStream) {
|
||||
const stream = convertPlexToMovie(plexStream);
|
||||
stream.mediaInfo = convertStreamToMediaInfo(plexStream.Media);
|
||||
stream.player = convertStreamToPlayer(plexStream.Player);
|
||||
stream.user = convertStreamToUser(plexStream.User);
|
||||
stream.playback = new ConvertStreamToPlayback(plexStream.Media.Part);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
module.exports = convertPlexToStream;
|
||||
21
seasoned_api/src/plex/convertStreamToMediaInfo.js
Normal file
21
seasoned_api/src/plex/convertStreamToMediaInfo.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const MediaInfo = require('src/media_classes/mediaInfo');
|
||||
|
||||
function convertStreamToMediaInfo(plexStream) {
|
||||
const mediaInfo = new MediaInfo();
|
||||
|
||||
mediaInfo.duration = plexStream.duration;
|
||||
mediaInfo.height = plexStream.height;
|
||||
mediaInfo.width = plexStream.width;
|
||||
if (plexStream.bitrate) {
|
||||
mediaInfo.bitrate = plexStream.bitrate;
|
||||
}
|
||||
mediaInfo.resolution = plexStream.videoResolution;
|
||||
mediaInfo.framerate = plexStream.videoFrameRate;
|
||||
mediaInfo.protocol = plexStream.protocol;
|
||||
mediaInfo.container = plexStream.container;
|
||||
mediaInfo.audioCodec = plexStream.audioCodec;
|
||||
|
||||
return mediaInfo;
|
||||
}
|
||||
|
||||
module.exports = convertStreamToMediaInfo;
|
||||
14
seasoned_api/src/plex/hookDump.js
Normal file
14
seasoned_api/src/plex/hookDump.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* @Author: KevinMidboe
|
||||
* @Date: 2017-05-03 23:26:46
|
||||
* @Last Modified by: KevinMidboe
|
||||
* @Last Modified time: 2017-05-03 23:27:59
|
||||
*/
|
||||
|
||||
const configuration = require('src/config/configuration').getInstance();
|
||||
|
||||
function hookDumpController(req, res) {
|
||||
console.log(req);
|
||||
}
|
||||
|
||||
module.exports = hookDumpController;
|
||||
26
seasoned_api/src/plex/mailTemplate.js
Normal file
26
seasoned_api/src/plex/mailTemplate.js
Normal file
@@ -0,0 +1,26 @@
|
||||
class mailTemplate {
|
||||
|
||||
constructor(mediaItem) {
|
||||
this.mediaItem = mediaItem;
|
||||
this.posterURL = 'https://image.tmdb.org/t/p/w600/';
|
||||
}
|
||||
|
||||
toText() {
|
||||
return this.mediaItem.title + ' (' + this.mediaItem.year + ')'; // plain text body
|
||||
}
|
||||
|
||||
toHTML() {
|
||||
const info = {
|
||||
name: this.mediaItem.title,
|
||||
year: '(' + this.mediaItem.year + ')',
|
||||
poster: this.posterURL + this.mediaItem.poster
|
||||
}
|
||||
|
||||
return `
|
||||
<h1>${info.name} ${info.year}</h1>
|
||||
<img src="${info.poster}">
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = mailTemplate;
|
||||
53
seasoned_api/src/plex/plexRepository.js
Normal file
53
seasoned_api/src/plex/plexRepository.js
Normal file
@@ -0,0 +1,53 @@
|
||||
const assert = require('assert');
|
||||
const convertPlexToMovie = require('src/plex/convertPlexToMovie');
|
||||
const convertPlexToStream = require('src/plex/convertPlexToStream');
|
||||
const configuration = require('src/config/configuration').getInstance();
|
||||
const TMDB = require('src/tmdb/tmdb');
|
||||
const tmdb = new TMDB(configuration.get('tmdb', 'apiKey'));
|
||||
var rp = require('request-promise');
|
||||
|
||||
class PlexRepository {
|
||||
|
||||
searchMedia(query) {
|
||||
var options = {
|
||||
uri: 'http://10.0.0.41:32400/search?query=' + query,
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
json: true
|
||||
}
|
||||
|
||||
return rp(options)
|
||||
.then((result) => {
|
||||
return result.MediaContainer.Metadata.map(convertPlexToMovie);
|
||||
})
|
||||
.catch((err) => {
|
||||
throw new Error(err);
|
||||
})
|
||||
}
|
||||
|
||||
nowPlaying() {
|
||||
var options = {
|
||||
uri: 'http://10.0.0.41:32400/status/sessions',
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
json: true
|
||||
}
|
||||
|
||||
return rp(options)
|
||||
.then((result) => {
|
||||
if (result.MediaContainer.size > 0) {
|
||||
var playing = result.MediaContainer.Video.map(convertPlexToStream);
|
||||
return {'size': Object.keys(playing).length, 'video': playing };
|
||||
} else {
|
||||
return { 'size': 0, 'video': [] };
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
throw new Error(err);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PlexRepository;
|
||||
123
seasoned_api/src/plex/requestRepository.js
Normal file
123
seasoned_api/src/plex/requestRepository.js
Normal file
@@ -0,0 +1,123 @@
|
||||
const assert = require('assert');
|
||||
const PlexRepository = require('src/plex/plexRepository');
|
||||
const plexRepository = new PlexRepository();
|
||||
const convertPlexToMovie = require('src/plex/convertPlexToMovie');
|
||||
const configuration = require('src/config/configuration').getInstance();
|
||||
const TMDB = require('src/tmdb/tmdb');
|
||||
const tmdb = new TMDB(configuration.get('tmdb', 'apiKey'));
|
||||
var Promise = require('bluebird');
|
||||
var rp = require('request-promise');
|
||||
|
||||
const MailTemplate = require('src/plex/mailTemplate')
|
||||
|
||||
var pythonShell = require('python-shell');
|
||||
const nodemailer = require('nodemailer');
|
||||
|
||||
|
||||
class RequestRepository {
|
||||
|
||||
searchRequest(query, page, type) {
|
||||
return Promise.resolve()
|
||||
.then(() => tmdb.search(query, page, type))
|
||||
.then((tmdbMovies) => {
|
||||
return Promise.resolve()
|
||||
.then(() => plexRepository.searchMedia(query))
|
||||
.then((plexMedia) => {
|
||||
return Promise.each(tmdbMovies, function(tmdbMovie) {
|
||||
return Promise.each(plexMedia, function(plexMovie) {
|
||||
if (tmdbMovie.title == plexMovie.title && tmdbMovie.year == plexMovie.year) {
|
||||
tmdbMovie.matchedInPlex = true;
|
||||
console.log(tmdbMovie.title + ' : ' + tmdbMovie.year);
|
||||
}
|
||||
return tmdbMovie;
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
return error;
|
||||
});
|
||||
}
|
||||
|
||||
lookup(identifier, type = 'movie') {
|
||||
if (type === 'movie') { type = 'movieInfo'}
|
||||
else if (type === 'tv') { type = 'tvInfo'}
|
||||
return Promise.resolve()
|
||||
.then(() => tmdb.lookup(identifier, type))
|
||||
.then((tmdbMovie) => {
|
||||
return Promise.resolve(plexRepository.searchMedia(tmdbMovie.title))
|
||||
.then((plexMovies) => {
|
||||
for (var i = 0; i < plexMovies.length; i++) {
|
||||
if (tmdbMovie.title === plexMovies[i].title && tmdbMovie.year === plexMovies[i].year) {
|
||||
tmdbMovie.matchedInPlex = true;
|
||||
return tmdbMovie;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
return error;
|
||||
});
|
||||
return tmdbMovie;
|
||||
});
|
||||
}
|
||||
|
||||
sendRequest(identifier) {
|
||||
// TODO try a cache hit on the movie item
|
||||
|
||||
tmdb.lookup(identifier).then(movie => {
|
||||
console.log(movie.title)
|
||||
|
||||
|
||||
// create reusable transporter object using the default SMTP transport
|
||||
let transporter = nodemailer.createTransport({
|
||||
host: configuration.get('mail', 'host'),
|
||||
port: 26,
|
||||
ignoreTLS: true,
|
||||
tls :{rejectUnauthorized: false},
|
||||
secure: false, // secure:true for port 465, secure:false for port 587
|
||||
auth: {
|
||||
user: configuration.get('mail', 'user'),
|
||||
pass: configuration.get('mail', 'password')
|
||||
}
|
||||
});
|
||||
|
||||
const mailTemplate = new MailTemplate(movie)
|
||||
|
||||
// setup email data with unicode symbols
|
||||
let mailOptions = {
|
||||
// TODO get the mail adr from global location (easy to add)
|
||||
from: 'MovieRequester <support@kevinmidboe.com>', // sender address
|
||||
to: 'kevin.midboe@gmail.com', // list of receivers
|
||||
subject: 'Download request', // Subject line
|
||||
text: mailTemplate.toText(),
|
||||
html: mailTemplate.toHTML()
|
||||
};
|
||||
|
||||
// send mail with defined transport object
|
||||
transporter.sendMail(mailOptions, (error, info) => {
|
||||
if (error) {
|
||||
return console.log(error);
|
||||
}
|
||||
console.log('Message %s sent: %s', info.messageId, info.response);
|
||||
});
|
||||
|
||||
// var options = {
|
||||
// args: [movie.title, movie.year, movie.poster]
|
||||
// }
|
||||
|
||||
// pythonShell.run('sendRequest.py', options, function (err, results) {
|
||||
// if (err) throw err;
|
||||
// // TODO Add error handling!! RequestRepository.ERROR
|
||||
// // results is an array consisting of messages collected during execution
|
||||
|
||||
// console.log('results: %j', results)
|
||||
// })
|
||||
})
|
||||
|
||||
return Promise.resolve();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = RequestRepository;
|
||||
10
seasoned_api/src/plex/stream/convertStreamToPlayback.js
Normal file
10
seasoned_api/src/plex/stream/convertStreamToPlayback.js
Normal file
@@ -0,0 +1,10 @@
|
||||
class convertStreamToPlayback {
|
||||
constructor(plexStream) {
|
||||
this.bitrate = plexStream.bitrate;
|
||||
this.width = plexStream.width;
|
||||
this.height = plexStream.height;
|
||||
this.decision = plexStream.decision;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = convertStreamToPlayback;
|
||||
13
seasoned_api/src/plex/stream/convertStreamToPlayer.js
Normal file
13
seasoned_api/src/plex/stream/convertStreamToPlayer.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const Player = require('src/media_classes/player');
|
||||
|
||||
function convertStreamToPlayer(plexStream) {
|
||||
const player = new Player(plexStream.device, plexStream.address);
|
||||
player.platform = plexStream.platform;
|
||||
player.product = plexStream.product;
|
||||
player.title = plexStream.title;
|
||||
player.state = plexStream.state;
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
module.exports = convertStreamToPlayer;
|
||||
7
seasoned_api/src/plex/stream/convertStreamToUser.js
Normal file
7
seasoned_api/src/plex/stream/convertStreamToUser.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const User = require('src/media_classes/user');
|
||||
|
||||
function convertStreamToUser(plexStream) {
|
||||
return new User(plexStream.id, plexStream.title);
|
||||
}
|
||||
|
||||
module.exports = convertStreamToUser;
|
||||
7
seasoned_api/src/seasoned/stray.js
Normal file
7
seasoned_api/src/seasoned/stray.js
Normal file
@@ -0,0 +1,7 @@
|
||||
class Stray {
|
||||
constructor(id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Stray;
|
||||
66
seasoned_api/src/seasoned/strayRepository.js
Normal file
66
seasoned_api/src/seasoned/strayRepository.js
Normal file
@@ -0,0 +1,66 @@
|
||||
const assert = require('assert');
|
||||
const Stray = require('src/seasoned/stray');
|
||||
const establishedDatabase = require('src/database/database');
|
||||
var pythonShell = require('python-shell');
|
||||
|
||||
function foo(e) {
|
||||
throw('Foooo');
|
||||
}
|
||||
|
||||
class StrayRepository {
|
||||
|
||||
constructor(database) {
|
||||
this.database = database || establishedDatabase;
|
||||
this.queries = {
|
||||
'read': 'SELECT * FROM stray_eps WHERE id = ?',
|
||||
'readAll': 'SELECT id, name, season, episode, verified FROM stray_eps',
|
||||
'readAllFiltered': 'SELECT id, name, season, episode, verified FROM stray_eps WHERE verified = ',
|
||||
'checkVerified': 'SELECT id FROM stray_eps WHERE verified = 0 AND id = ?',
|
||||
'verify': 'UPDATE stray_eps SET verified = 1 WHERE id = ?',
|
||||
};
|
||||
}
|
||||
|
||||
read(strayId) {
|
||||
return this.database.get(this.queries.read, strayId).then((row) => {
|
||||
assert.notEqual(row, undefined, `Could not find list with id ${strayId}.`);
|
||||
return row;
|
||||
})
|
||||
}
|
||||
|
||||
readAll(verified = null, page = 1) {
|
||||
var dbSearchQuery = this.queries.readAll;
|
||||
if (verified != null) {
|
||||
dbSearchQuery = this.queries.readAllFiltered + verified.toString();
|
||||
}
|
||||
return this.database.all(dbSearchQuery).then(rows =>
|
||||
rows.map((row) => {
|
||||
const stray = new Stray(row.id);
|
||||
stray.name = row.name;
|
||||
stray.season = row.season;
|
||||
stray.episode = row.episode;
|
||||
stray.verified = row.verified;
|
||||
return stray;
|
||||
}))
|
||||
}
|
||||
|
||||
verifyStray(strayId) {
|
||||
return this.database.get(this.queries.checkVerified, strayId).then((row) => {
|
||||
assert.notEqual(row, undefined, `Stray '${strayId}' already verified.`);
|
||||
|
||||
var options = {
|
||||
args: [strayId]
|
||||
}
|
||||
|
||||
pythonShell.run('moveSeasoned.py', options, function (err, results) {
|
||||
if (err) throw err;
|
||||
// TODO Add error handling!! StrayRepository.ERROR
|
||||
// results is an array consisting of messages collected during execution
|
||||
console.log('results: %j', results);
|
||||
});
|
||||
|
||||
return this.database.run(this.queries.verify, strayId);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StrayRepository;
|
||||
30
seasoned_api/src/tmdb/convertTmdbToMovie.js
Normal file
30
seasoned_api/src/tmdb/convertTmdbToMovie.js
Normal file
@@ -0,0 +1,30 @@
|
||||
const Movie = require('src/media_classes/movie');
|
||||
|
||||
function convertTmdbToMovie(tmdbMovie) {
|
||||
const movie = new Movie();
|
||||
movie.id = tmdbMovie.id;
|
||||
if (tmdbMovie.media_type === 'movie' || tmdbMovie.release_date !== undefined) {
|
||||
movie.title = tmdbMovie.title;
|
||||
movie.type = 'movie';
|
||||
|
||||
if (tmdbMovie.release_date !== undefined) {
|
||||
movie.release_date = new Date(tmdbMovie.release_date);
|
||||
movie.year = movie.release_date.getFullYear();
|
||||
}
|
||||
} else if (tmdbMovie.first_air_date !== undefined) {
|
||||
movie.title = tmdbMovie.name;
|
||||
movie.type = 'show';
|
||||
if (tmdbMovie.first_air_date !== undefined) {
|
||||
movie.release_date = new Date(tmdbMovie.first_air_date);
|
||||
movie.year = movie.release_date.getFullYear();
|
||||
}
|
||||
}
|
||||
|
||||
movie.poster = tmdbMovie.poster_path;
|
||||
movie.background = tmdbMovie.backdrop_path;
|
||||
movie.overview = tmdbMovie.overview;
|
||||
|
||||
return movie;
|
||||
}
|
||||
|
||||
module.exports = convertTmdbToMovie;
|
||||
67
seasoned_api/src/tmdb/tmdb.js
Normal file
67
seasoned_api/src/tmdb/tmdb.js
Normal file
@@ -0,0 +1,67 @@
|
||||
const moviedb = require('moviedb');
|
||||
const convertTmdbToMovie = require('src/tmdb/convertTmdbToMovie');
|
||||
var methodTypes = { 'movie': 'searchMovie', 'tv': 'searchTv', 'multi': 'searchMulti', 'movieInfo': 'movieInfo',
|
||||
'tvInfo': 'tvInfo' };
|
||||
|
||||
class TMDB {
|
||||
constructor(apiKey, tmdbLibrary) {
|
||||
this.tmdbLibrary = tmdbLibrary || moviedb(apiKey);
|
||||
}
|
||||
|
||||
search(text, page = 1, type = 'multi') {
|
||||
const query = { query: text, page };
|
||||
return Promise.resolve()
|
||||
.then(() => this.tmdb(type, query))
|
||||
.catch(() => { throw new Error('Could not search for movies.'); })
|
||||
.then((reponse) => {
|
||||
try {
|
||||
return reponse.results.filter(function(item) {
|
||||
return (item.popularity >= 1.15)
|
||||
}).map(convertTmdbToMovie);
|
||||
} catch (parseError) {
|
||||
throw new Error('Could not parse result.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a specific movie by id from TMDB.
|
||||
* @param {Number} identifier of the movie you want to retrieve
|
||||
* @returns {Promise} succeeds if movie was found
|
||||
*/
|
||||
lookup(identifier, type = 'movie') {
|
||||
if (type === 'movie') { type = 'movieInfo'}
|
||||
else if (type === 'tv') { type = 'tvInfo'}
|
||||
const query = { id: identifier };
|
||||
return Promise.resolve()
|
||||
.then(() => this.tmdb(type, query))
|
||||
.catch(() => { throw new Error('Could not find a movie with that id.'); })
|
||||
.then((response) => {
|
||||
try {
|
||||
return convertTmdbToMovie(response);
|
||||
} catch (parseError) {
|
||||
throw new Error('Could not parse movie.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tmdb(method, argument) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const callback = (error, reponse) => {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
resolve(reponse);
|
||||
};
|
||||
|
||||
if (!argument) {
|
||||
this.tmdbLibrary[methodTypes[method]](callback);
|
||||
} else {
|
||||
this.tmdbLibrary[methodTypes[method]](argument, callback);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TMDB;
|
||||
4
seasoned_api/src/webserver/access.log
Normal file
4
seasoned_api/src/webserver/access.log
Normal file
@@ -0,0 +1,4 @@
|
||||
::1 - - [02/Jun/2017:06:34:49 +0000] "GET /api/v1/seasoned/all HTTP/1.1" 200 1016 "-" "curl/7.51.0"
|
||||
::1 - - [02/Jun/2017:06:34:50 +0000] "GET /api/v1/seasoned/all HTTP/1.1" 200 1016 "-" "curl/7.51.0"
|
||||
::1 - - [02/Jun/2017:06:45:59 +0000] "GET /api/v1/seasoned/all HTTP/1.1" 200 1016 "-" "curl/7.51.0"
|
||||
::1 - - [02/Jun/2017:06:48:30 +0000] "GET /api/v1/seasoned/all HTTP/1.1" 200 1016 "-" "curl/7.51.0"
|
||||
52
seasoned_api/src/webserver/app.js
Normal file
52
seasoned_api/src/webserver/app.js
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
var express = require('express'); // call express
|
||||
var app = express(); // define our app using express
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
// this will let us get the data from a POST
|
||||
// configure app to use bodyParser()
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
|
||||
|
||||
var port = 31459; // set our port
|
||||
var router = express.Router();
|
||||
var allowedOrigins = ['https://kevinmidboe.com', 'http://localhost:8080']
|
||||
|
||||
|
||||
router.use(function(req, res, next) {
|
||||
console.log('Something is happening.');
|
||||
var origin = req.headers.origin;
|
||||
if (allowedOrigins.indexOf(origin) > -1) {
|
||||
res.setHeader('Access-Control-Allow-Origin', origin);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
router.get('/', function(req, res) {
|
||||
res.json({ message: 'hooray! welcome to this api!' });
|
||||
});
|
||||
|
||||
|
||||
router.get('/v1/seasoned/all', require('./controllers/seasoned/readStrays.js'));
|
||||
router.get('/v1/seasoned/:strayId', require('./controllers/seasoned/strayById.js'));
|
||||
router.post('/v1/seasoned/verify/:strayId', require('./controllers/seasoned/verifyStray.js'));
|
||||
|
||||
router.get('/v1/plex/search', require('./controllers/plex/searchMedia.js'));
|
||||
router.get('/v1/plex/playing', require('./controllers/plex/plexPlaying.js'));
|
||||
router.get('/v1/plex/request', require('./controllers/plex/searchRequest.js'));
|
||||
router.get('/v1/plex/request/:mediaId', require('./controllers/plex/readRequest.js'));
|
||||
router.post('/v1/plex/request/:mediaId', require('./controllers/plex/submitRequest.js'));
|
||||
router.get('/v1/plex/hook', require('./controllers/plex/hookDump.js'));
|
||||
|
||||
router.get('/v1/tmdb/search', require('./controllers/tmdb/searchMedia.js'));
|
||||
router.get('/v1/tmdb/:mediaId', require('./controllers/tmdb/readMedia.js'));
|
||||
|
||||
router.post('/v1/git/dump', require('./controllers/git/dumpHook.js'));
|
||||
|
||||
|
||||
// REGISTER OUR ROUTES -------------------------------
|
||||
// all of our routes will be prefixed with /api
|
||||
app.use('/api', router);
|
||||
|
||||
module.exports = app;
|
||||
15
seasoned_api/src/webserver/controllers/git/dumpHook.js
Normal file
15
seasoned_api/src/webserver/controllers/git/dumpHook.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const configuration = require('src/config/configuration').getInstance();
|
||||
const GitRepository = require('src/git/gitRepository');
|
||||
const gitRepository = new GitRepository();
|
||||
|
||||
function dumpHookController(req, res) {
|
||||
gitRepository.dumpHook(req.body)
|
||||
.then(() => {
|
||||
res.status(200);
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500);
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = dumpHookController;
|
||||
14
seasoned_api/src/webserver/controllers/plex/hookDump.js
Normal file
14
seasoned_api/src/webserver/controllers/plex/hookDump.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* @Author: KevinMidboe
|
||||
* @Date: 2017-05-03 23:26:46
|
||||
* @Last Modified by: KevinMidboe
|
||||
* @Last Modified time: 2017-05-03 23:27:59
|
||||
*/
|
||||
|
||||
const configuration = require('src/config/configuration').getInstance();
|
||||
|
||||
function hookDumpController(req, res) {
|
||||
console.log(req);
|
||||
}
|
||||
|
||||
module.exports = hookDumpController;
|
||||
14
seasoned_api/src/webserver/controllers/plex/plexPlaying.js
Normal file
14
seasoned_api/src/webserver/controllers/plex/plexPlaying.js
Normal file
@@ -0,0 +1,14 @@
|
||||
const PlexRepository = require('src/plex/plexRepository');
|
||||
const plexRepository = new PlexRepository();
|
||||
|
||||
function playingController(req, res) {
|
||||
plexRepository.nowPlaying()
|
||||
.then((movies) => {
|
||||
res.send(movies);
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).send({success: false, error: error.message });
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = playingController;
|
||||
21
seasoned_api/src/webserver/controllers/plex/readRequest.js
Normal file
21
seasoned_api/src/webserver/controllers/plex/readRequest.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const RequestRepository = require('src/plex/requestRepository');
|
||||
const requestRepository = new RequestRepository();
|
||||
|
||||
/**
|
||||
* Controller: Retrieve information for a movie
|
||||
* @param {Request} req http request variable
|
||||
* @param {Response} res
|
||||
* @returns {Callback}
|
||||
*/
|
||||
function readRequestController(req, res) {
|
||||
const mediaId = req.params.mediaId;
|
||||
const { type } = req.query;
|
||||
requestRepository.lookup(mediaId, type)
|
||||
.then((movies) => {
|
||||
res.send(movies);
|
||||
}).catch((error) => {
|
||||
res.status(404).send({ success: false, error: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = readRequestController;
|
||||
17
seasoned_api/src/webserver/controllers/plex/searchMedia.js
Normal file
17
seasoned_api/src/webserver/controllers/plex/searchMedia.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const PlexRepository = require('src/plex/plexRepository');
|
||||
const plexRepository = new PlexRepository();
|
||||
|
||||
function searchMediaController(req, res) {
|
||||
const { query, page } = req.query;
|
||||
console.log(query);
|
||||
|
||||
plexRepository.searchMedia(query)
|
||||
.then((movies) => {
|
||||
res.send(movies);
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).send({success: false, error: error.message });
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = searchMediaController;
|
||||
24
seasoned_api/src/webserver/controllers/plex/searchRequest.js
Normal file
24
seasoned_api/src/webserver/controllers/plex/searchRequest.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const RequestRepository = require('src/plex/requestRepository.js');
|
||||
const requestRepository = new RequestRepository();
|
||||
|
||||
function searchRequestController(req, res) {
|
||||
const { query, page, type } = req.query;
|
||||
console.log('searchReq: ' + query, page, type);
|
||||
|
||||
requestRepository.searchRequest(query, page, type)
|
||||
.then((movies) => {
|
||||
// Verify that respond has content, if so send the content back
|
||||
if (movies.length > 0 && movies != null) {
|
||||
res.send(movies);
|
||||
}
|
||||
// If no content was found, send 404 status and error message
|
||||
else {
|
||||
res.status(404).send({success: false, error: 'Search query did not return any results.'})
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).send({success: false, error: error.message });
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = searchRequestController;
|
||||
24
seasoned_api/src/webserver/controllers/plex/submitRequest.js
Normal file
24
seasoned_api/src/webserver/controllers/plex/submitRequest.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const RequestRepository = require('src/plex/requestRepository.js');
|
||||
const requestRepository = new RequestRepository();
|
||||
|
||||
/**
|
||||
* Controller: POST a media id to be donwloaded
|
||||
* @param {Request} req http request variable
|
||||
* @param {Response} res
|
||||
* @returns {Callback}
|
||||
*/
|
||||
|
||||
function submitRequestController(req, res) {
|
||||
// This is the id that is the param of the url
|
||||
const id = req.params.mediaId;
|
||||
|
||||
requestRepository.sendRequest(id)
|
||||
.then(() => {
|
||||
res.send({ success: true, message: 'Media item sucessfully requested!' });
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).send({ success: false, error: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = submitRequestController;
|
||||
@@ -0,0 +1,16 @@
|
||||
const StrayRepository = require('src/seasoned/strayRepository');
|
||||
const strayRepository = new StrayRepository();
|
||||
|
||||
|
||||
function readStraysController(req, res) {
|
||||
const { verified, page } = req.query;
|
||||
strayRepository.readAll(verified, page)
|
||||
.then((strays) => {
|
||||
res.send(strays);
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).send({success: false, error: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = readStraysController;
|
||||
17
seasoned_api/src/webserver/controllers/seasoned/strayById.js
Normal file
17
seasoned_api/src/webserver/controllers/seasoned/strayById.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const configuration = require('src/config/configuration').getInstance();
|
||||
const StrayRepository = require('src/seasoned/strayRepository');
|
||||
const strayRepository = new StrayRepository();
|
||||
|
||||
function strayByIdController(req, res) {
|
||||
const id = req.params.strayId;
|
||||
|
||||
strayRepository.read(id)
|
||||
.then((stray) => {
|
||||
res.send(stray);
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).send({ success: false, error: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = strayByIdController;
|
||||
@@ -0,0 +1,16 @@
|
||||
const StrayRepository = require('src/seasoned/strayRepository');
|
||||
const strayRepository = new StrayRepository();
|
||||
|
||||
function verifyStrayController(req, res) {
|
||||
const id = req.params.strayId;
|
||||
|
||||
strayRepository.verifyStray(id)
|
||||
.then(() => {
|
||||
res.send({ success: true, message: 'Episode verified' });
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).send({ success: false, error: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = verifyStrayController;
|
||||
22
seasoned_api/src/webserver/controllers/tmdb/readMedia.js
Normal file
22
seasoned_api/src/webserver/controllers/tmdb/readMedia.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const configuration = require('src/config/configuration').getInstance();
|
||||
const TMDB = require('src/tmdb/tmdb');
|
||||
const tmdb = new TMDB(configuration.get('tmdb', 'apiKey'));
|
||||
|
||||
/**
|
||||
* Controller: Retrieve information for a movie
|
||||
* @param {Request} req http request variable
|
||||
* @param {Response} res
|
||||
* @returns {Callback}
|
||||
*/
|
||||
function readMediaController(req, res) {
|
||||
const mediaId = req.params.mediaId;
|
||||
const { type } = req.query;
|
||||
tmdb.lookup(mediaId, type)
|
||||
.then((movies) => {
|
||||
res.send(movies);
|
||||
}).catch((error) => {
|
||||
res.status(404).send({ success: false, error: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = readMediaController;
|
||||
29
seasoned_api/src/webserver/controllers/tmdb/searchMedia.js
Normal file
29
seasoned_api/src/webserver/controllers/tmdb/searchMedia.js
Normal file
@@ -0,0 +1,29 @@
|
||||
const configuration = require('src/config/configuration').getInstance();
|
||||
const TMDB = require('src/tmdb/tmdb');
|
||||
const tmdb = new TMDB(configuration.get('tmdb', 'apiKey'));
|
||||
|
||||
/**
|
||||
* Controller: Search for movies by query, page and optionally adult
|
||||
* @param {Request} req http request variable
|
||||
* @param {Response} res
|
||||
* @returns {Callback}
|
||||
*/
|
||||
function searchMoviesController(req, res) {
|
||||
const { query, page, type } = req.query;
|
||||
|
||||
Promise.resolve()
|
||||
.then(() => tmdb.search(query, page, type))
|
||||
.then((movies) => {
|
||||
if (movies.length > 0) {
|
||||
res.send(movies);
|
||||
} else {
|
||||
res.status(404).send({ success: false, error: 'Search query did not return any results.'})
|
||||
}
|
||||
res.send(movies);
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).send({ success: false, error: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = searchMoviesController;
|
||||
8
seasoned_api/src/webserver/server.js
Normal file
8
seasoned_api/src/webserver/server.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const config = require('src/config/configuration').getInstance();
|
||||
const app = require('./app');
|
||||
|
||||
module.exports = app.listen(config.get('webserver', 'port'), () => {
|
||||
console.log('seasonedAPI');
|
||||
console.log(`Database is located at ${config.get('database', 'host')}`);
|
||||
console.log(`Webserver is listening on ${config.get('webserver', 'port')}`);
|
||||
})
|
||||
1042
seasoned_api/yarn.lock
Normal file
1042
seasoned_api/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user