Client feature #14
2
.gitignore
vendored
2
.gitignore
vendored
@@ -8,3 +8,5 @@ node_modules
|
|||||||
*.pyc
|
*.pyc
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
webpage/js/env_variables.js
|
webpage/js/env_variables.js
|
||||||
|
client/dist
|
||||||
|
src/webserver/access.log
|
||||||
|
|||||||
@@ -7,15 +7,18 @@ class MovieObject {
|
|||||||
this.year = object.year;
|
this.year = object.year;
|
||||||
// Check if object.poster != undefined
|
// Check if object.poster != undefined
|
||||||
this.poster = object.poster;
|
this.poster = object.poster;
|
||||||
this.matchedInPlex = object.matchedInPlex
|
this.matchedInPlex = object.matchedInPlex;
|
||||||
|
this.overview = object.overview;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestExisting(id) {
|
requestExisting(movie) {
|
||||||
console.log('Exists', id)
|
console.log('Exists', movie)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestMovie(id) {
|
requestMovie(id) {
|
||||||
console.log(id);
|
fetch('http://localhost:31459/api/v1/plex/request/' + id, {
|
||||||
|
method: 'POST'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getElement() {
|
getElement() {
|
||||||
@@ -30,11 +33,13 @@ class MovieObject {
|
|||||||
returnList.push(<img src={posterPath}></img>);
|
returnList.push(<img src={posterPath}></img>);
|
||||||
|
|
||||||
if (this.matchedInPlex) {
|
if (this.matchedInPlex) {
|
||||||
returnList.push(<button onClick={() => this.requestExisting(this.id)}>Request anyway</button>)
|
returnList.push(<button onClick={() => this.requestExisting(this)}>Request anyway</button>)
|
||||||
} else {
|
} else {
|
||||||
returnList.push(<button onClick={() => this.requestMovie(this.id)}>Request</button>)
|
returnList.push(<button onClick={() => this.requestMovie(this)}>Request</button>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
returnList.push(<span>{this.overview}</span>);
|
||||||
|
|
||||||
returnList.push(<br></br>);
|
returnList.push(<br></br>);
|
||||||
return returnList;
|
return returnList;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,72 +2,101 @@ import React from 'react';
|
|||||||
|
|
||||||
import MovieObject from './MovieObject.jsx';
|
import MovieObject from './MovieObject.jsx';
|
||||||
|
|
||||||
|
// TODO add option for searching multi, movies or tv shows
|
||||||
|
|
||||||
class SearchRequest extends React.Component {
|
class SearchRequest extends React.Component {
|
||||||
constructor(props){
|
constructor(props){
|
||||||
super(props)
|
super(props)
|
||||||
|
// Constructor with states holding the search query and the element of reponse.
|
||||||
this.state = {
|
this.state = {
|
||||||
searchQuery: '',
|
searchQuery: '',
|
||||||
items: []
|
responseMovieList: null
|
||||||
|
}
|
||||||
|
|
||||||
|
this.URLs = {
|
||||||
|
request: 'https://apollo.kevinmidboe.com/api/v1/plex/request?query=',
|
||||||
|
sendRequest: 'https://apollo.kevinmidboe.com/api/v1/plex/request?query='
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(){
|
componentDidMount(){
|
||||||
var that = this;
|
var that = this;
|
||||||
fetch("https://apollo.kevinmidboe.com/api/v1/plex/request?query=interstellar")
|
this.setState({responseMovieList: null})
|
||||||
.then(response => response.json())
|
}
|
||||||
.then(data => this.setState({
|
|
||||||
items: data
|
// Handles all errors of the response of a fetch call
|
||||||
})
|
handleErrors(response) {
|
||||||
).catch(err => console.error('Error load: ', err.toString()));
|
if (!response.ok) {
|
||||||
|
throw Error(response.status);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleKeyPress(e) {
|
fetchQuery() {
|
||||||
|
let url = this.URLs.request + this.state.searchQuery
|
||||||
|
|
||||||
|
fetch(url)
|
||||||
|
// Check if the response is ok
|
||||||
|
.then(response => this.handleErrors(response))
|
||||||
|
.then(response => response.json()) // Convert to json object and pass to next then
|
||||||
|
.then(data => { // Parse the data of the JSON response
|
||||||
|
// If it is something here it updates the state variable with the HTML list of all
|
||||||
|
// movie objects that where returned by the search request
|
||||||
|
if (data.length > 0) {
|
||||||
|
this.setState({
|
||||||
|
responseMovieList: data.map(item => this.createMovieObjects(item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// If the --------
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error)
|
||||||
|
this.setState({
|
||||||
|
responseMovieList: <h1>Not Found</h1>
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('Error submit: ', error.toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the internal state of the query search field.
|
||||||
|
updateQueryState(event){
|
||||||
|
this.setState({
|
||||||
|
searchQuery: event.target.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// For checking if the enter key was pressed in the search field.
|
||||||
|
_handleQueryKeyPress(e) {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
this.fetchQuery();
|
this.fetchQuery();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchQuery() {
|
// When called passes the variable to MovieObject and calls it's interal function for
|
||||||
var query = 'https://apollo.kevinmidboe.com/api/v1/plex/request?query=' + this.state.searchQuery;
|
// generating the wanted HTML
|
||||||
|
createMovieObjects(item) {
|
||||||
fetch(query)
|
let movie = new MovieObject(item);
|
||||||
.then(response => response.json())
|
return movie.getElement();
|
||||||
.then(data => this.setState({
|
|
||||||
items: data
|
|
||||||
})
|
|
||||||
).catch(err => console.error('Error submit: ', err.toString()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printMovies(item) {
|
|
||||||
if (item != undefined) {
|
|
||||||
let a = new MovieObject(item);
|
|
||||||
return a.getElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
handleChange(event){
|
|
||||||
this.setState({
|
|
||||||
searchQuery: event.target.value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
return(
|
return(
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
onKeyPress={(event) => this._handleKeyPress(event)}
|
onKeyPress={(event) => this._handleQueryKeyPress(event)}
|
||||||
onChange={event => this.handleChange(event)}
|
onChange={event => this.updateQueryState(event)}
|
||||||
value={this.state.searchQuery}
|
value={this.state.searchQuery}
|
||||||
/>
|
/>
|
||||||
<button onClick={() => this.fetchQuery()}>Search</button>
|
<button onClick={() => this.fetchQuery()}>Search</button>
|
||||||
<br></br>
|
<br></br>
|
||||||
|
|
||||||
{this.state.searchQuery}
|
|
||||||
<br></br>
|
<br></br>
|
||||||
|
<span id='requestMovieList' ref='requestMovieList'>
|
||||||
{this.state.items.map((item) => this.printMovies(item))}
|
{this.state.responseMovieList}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1757,11 +1757,11 @@ inflight@^1.0.4:
|
|||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
inherits@2, inherits@2.0.3, inherits@~2.0.0, inherits@~2.0.1:
|
inherits@2, inherits@2.0.3, inherits@~2.0.0:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||||
|
|
||||||
inherits@2.0.1, inherits@^2.0.1:
|
inherits@2.0.1, inherits@^2.0.1, inherits@~2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
|
||||||
|
|
||||||
|
|||||||
57
seasonMover.py
Executable file
57
seasonMover.py
Executable file
@@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# @Author: KevinMidboe
|
||||||
|
# @Date: 2017-07-11 19:16:23
|
||||||
|
# @Last Modified by: KevinMidboe
|
||||||
|
# @Last Modified time: 2017-07-11 19:16:23
|
||||||
|
|
||||||
|
import fire, re, os
|
||||||
|
|
||||||
|
class seasonMover(object):
|
||||||
|
''' Moving multiple files to multiple folders with
|
||||||
|
identifer '''
|
||||||
|
workingDir = os.getcwd()
|
||||||
|
|
||||||
|
def create(self, name, interval):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def move(self, fileSyntax, folderName):
|
||||||
|
episodeRange = self.findInterval(fileSyntax)
|
||||||
|
|
||||||
|
self.motherMover(fileSyntax, folderName, episodeRange)
|
||||||
|
|
||||||
|
def findInterval(self, item):
|
||||||
|
if (re.search(r'\((.*)\)', item) is None):
|
||||||
|
raise ValueError('Need to declare an identifier e.g. (1..3) in: \n\t' + item)
|
||||||
|
|
||||||
|
start = int(re.search('\((\d+)\.\.', item).group(1))
|
||||||
|
end = int(re.search('\.\.(\d+)\)', item).group(1))
|
||||||
|
|
||||||
|
return list(range(start, end+1))
|
||||||
|
|
||||||
|
def removeUploadSign(self, file):
|
||||||
|
match = re.search('-[a-zA-Z\[\]\-]*.[a-z]{3}', file)
|
||||||
|
if match:
|
||||||
|
uploader = match.group(0)[:-4]
|
||||||
|
return re.sub(uploader, '', file)
|
||||||
|
|
||||||
|
return file
|
||||||
|
|
||||||
|
def motherMover(self, fileSyntax, folderName, episodeRange):
|
||||||
|
# Call for sub of fileList
|
||||||
|
# TODO check if range is same as folderContent
|
||||||
|
for episode in episodeRange:
|
||||||
|
leadingZeroNumber = "%02d" % episode
|
||||||
|
fileName = re.sub(r'\((.*)\)', leadingZeroNumber, fileSyntax)
|
||||||
|
|
||||||
|
oldPath = os.path.join(self.workingDir,fileName)
|
||||||
|
newFolder = os.path.join(self.workingDir, folderName + leadingZeroNumber)
|
||||||
|
newPath = os.path.join(newFolder, self.removeUploadSign(fileName))
|
||||||
|
|
||||||
|
os.makedirs(newFolder)
|
||||||
|
os.rename(oldPath, newPath)
|
||||||
|
# print(newFolder)
|
||||||
|
# print(oldPath + ' --> ' + newPath)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
fire.Fire(seasonMover)
|
||||||
@@ -8,6 +8,8 @@ const tmdb = new TMDB(configuration.get('tmdb', 'apiKey'));
|
|||||||
var Promise = require('bluebird');
|
var Promise = require('bluebird');
|
||||||
var rp = require('request-promise');
|
var rp = require('request-promise');
|
||||||
|
|
||||||
|
var pythonShell = require('python-shell');
|
||||||
|
|
||||||
class RequestRepository {
|
class RequestRepository {
|
||||||
|
|
||||||
searchRequest(query, page, type) {
|
searchRequest(query, page, type) {
|
||||||
@@ -55,6 +57,30 @@ class RequestRepository {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendRequest(identifier) {
|
||||||
|
// TODO try a cache hit on the movie item
|
||||||
|
|
||||||
|
console.log(identifier)
|
||||||
|
tmdb.lookup(identifier).then(movie => {
|
||||||
|
console.log(movie.title)
|
||||||
|
|
||||||
|
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 true;
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = RequestRepository;
|
module.exports = RequestRepository;
|
||||||
@@ -32,7 +32,7 @@ router.get('/v1/plex/search', require('./controllers/plex/searchMedia.js'));
|
|||||||
router.get('/v1/plex/playing', require('./controllers/plex/plexPlaying.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', require('./controllers/plex/searchRequest.js'));
|
||||||
router.get('/v1/plex/request/:mediaId', require('./controllers/plex/readRequest.js'));
|
router.get('/v1/plex/request/:mediaId', require('./controllers/plex/readRequest.js'));
|
||||||
// router.post('/v1/plex/request/:mediaId', require('./controllers/plex/submitRequest.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/plex/hook', require('./controllers/plex/hookDump.js'));
|
||||||
|
|
||||||
router.get('/v1/tmdb/search', require('./controllers/tmdb/searchMedia.js'));
|
router.get('/v1/tmdb/search', require('./controllers/tmdb/searchMedia.js'));
|
||||||
|
|||||||
24
src/webserver/controllers/plex/submitRequest.js
Normal file
24
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;
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
const configuration = require('src/config/configuration').getInstance();
|
|
||||||
const StrayRepository = require('src/seasoned/strayRepository');
|
const StrayRepository = require('src/seasoned/strayRepository');
|
||||||
const strayRepository = new StrayRepository();
|
const strayRepository = new StrayRepository();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user