potete chiudere l'internet PORCODIO

This commit is contained in:
matteo
2019-03-04 17:46:10 +01:00
49 changed files with 1696 additions and 615 deletions
+103 -5
View File
@@ -43,10 +43,62 @@ low(new FileAsync(__dirname + '/db.json')) // production
}); });
// ============== // ==============
// OPTIONS /globpop
app.options('/globpop', (req, res) => {
res.set({
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': ' GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Max-Age': 600
})
res.status(200).end();
})
// GET /globpop // GET /globpop
app.get('/globpop', (req, res) => { app.get('/globpop', (req, res) => {
req.query.id ? res.send(req.query.id) : res.status(400).send('Bad Request'); res.set({
'Access-Control-Allow-Origin': '*'
});
const videos1 = db.get('videos');
let videos = JSON.parse(JSON.stringify(videos1.values()));
if (req.query.id) {
let tmpRes;
let row = videos1.getById(req.query.id);
if (row.value()) {
let tmpA = videos.filter(x => x.id !== req.query.id).map(x => {
return {
"videoID": x.id,
"timesWatched": x.timesWatched,
"prevalentReason": x.reason.sort((a, b) => a.timesWatched - b.timesWatched).reverse()[0].reason,
"lastSelected": x.lastWatched
}
})
tmpRes = {
"site": "site1854.tw.cs.unibo.it",
"recommender": req.query.id.toString(),
"lastWatched": row.value().lastWatched,
"recomended": tmpA
}
}
else {
let tmpA = videos.map(x => {
return {
"videoID": x.id,
"timesWatched": x.timesWatched,
"prevalentReason": x.reason.sort((a, b) => a.timesWatched - b.timesWatched).reverse()[0].reason,
"lastSelected": x.lastWatched
}
})
tmpRes = {
"site": "site1854.tw.cs.unibo.it",
"recommender": req.query.id.toString(),
"lastWatched": "Never Watched",
"recomended": tmpA
}
}
res.send(tmpRes)
} else
res.status(400).send('Bad Request');
}); });
// OPTIONS /videotrack // OPTIONS /videotrack
@@ -62,8 +114,32 @@ low(new FileAsync(__dirname + '/db.json')) // production
// GET /videotrack // GET /videotrack
app.get('/videotrack', (req, res) => { app.get('/videotrack', (req, res) => {
const videos = db.get('videos'); res.set({
req.query.id ? res.send(videos.getById(req.query.id).value()) : res.send(videos.value()) 'Access-Control-Allow-Origin': '*'
});
const videos1 = db.get('videos').values();
let videos = JSON.parse(JSON.stringify(videos1));
console.log(req.query.prevId)
if (req.query.prevId) {
console.log(videos.length)
let tmp = [];
for (let i = 0; i < videos.length; i++) {
console.log(videos[i]);
for (let j = 0; j < videos[i].reason.length; j++) {
console.log(videos[i].reason[j])
if (videos[i].reason[j].prevId === req.query.prevId) {
let tmp2 = {
...videos[i].reason[j],
'id': videos[i].id
}
tmp.push(tmp2);
}
}
}
res.send(tmp)
} else {
res.send(videos)
}
}); });
// POST /videotrack // POST /videotrack
@@ -71,21 +147,40 @@ low(new FileAsync(__dirname + '/db.json')) // production
res.set({ res.set({
'Access-Control-Allow-Origin': '*' 'Access-Control-Allow-Origin': '*'
}); });
let setReason = function (reason = 'Starter', oldArr = [], prevId) {
let j = oldArr.findIndex(el => el.prevId === prevId && el.reason === reason)
if (j >= 0) {
++oldArr[j].timesWatched;
return oldArr
}
else {
let tmp = {
prevId,
reason,
timesWatched: 1
}
oldArr.push(tmp)
return oldArr
}
}
const videos = db.get('videos'); const videos = db.get('videos');
const row = videos.getById(req.body.id); const row = videos.getById(req.body.id);
if (row.value()) { if (row.value()) {
row row
.update('timesWatched', (n) => ++n) .update('timesWatched', (n) => ++n)
.update('lastWatched', () => new Date().toISOString()) .update('lastWatched', () => new Date().toISOString())
.update('reason', (n) => setReason(req.body.reason, n, req.body.prevId))
.write() .write()
.then(output => res.send(output)) .then(output => res.send(200, output))
} else { } else {
let newRow = { let newRow = {
"id": req.body.id.toString(), "id": req.body.id.toString(),
"timesWatched": 1, "timesWatched": 1,
"lastWatched": new Date().toISOString(), "lastWatched": new Date().toISOString(),
"reason": setReason(req.body.reason, [], req.body.prevId)
} }
videos.push(newRow).last().write().then(output => res.send(200,output)) videos.push(newRow).last().write().then(output => res.send(200, output))
} }
}); });
@@ -94,5 +189,8 @@ low(new FileAsync(__dirname + '/db.json')) // production
}) })
.then(() => { .then(() => {
app.use(express.static(__dirname + '/build')); // serve static build site app.use(express.static(__dirname + '/build')); // serve static build site
app.get('*', (req, res) => {
res.sendFile(__dirname + '/build/index.html');
});
app.listen(8000, () => console.log('listen on 8000')); // bind to port 8000 as required from specs app.listen(8000, () => console.log('listen on 8000')); // bind to port 8000 as required from specs
}); });
+8 -22
View File
@@ -1,6 +1,6 @@
{ {
"name": "hazetv-alfatube-1", "name": "hazetv-alfatube-1",
"version": "0.1.0", "version": "0.1.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -5879,13 +5879,11 @@
}, },
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@@ -5898,18 +5896,15 @@
}, },
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"bundled": true, "bundled": true
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@@ -6012,8 +6007,7 @@
}, },
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"bundled": true, "bundled": true
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@@ -6023,7 +6017,6 @@
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@@ -6036,20 +6029,17 @@
"minimatch": { "minimatch": {
"version": "3.0.4", "version": "3.0.4",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
}, },
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"bundled": true, "bundled": true
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.2.4", "version": "2.2.4",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.1", "safe-buffer": "^5.1.1",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@@ -6066,7 +6056,6 @@
"mkdirp": { "mkdirp": {
"version": "0.5.1", "version": "0.5.1",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@@ -6139,8 +6128,7 @@
}, },
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true, "bundled": true
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@@ -6150,7 +6138,6 @@
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@@ -6256,7 +6243,6 @@
"string-width": { "string-width": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
+1 -1
View File
@@ -43,7 +43,7 @@
"browserslist": [ "browserslist": [
">0.2%", ">0.2%",
"not dead", "not dead",
"not ie <= 11", "not ie <= 8",
"not op_mini all" "not op_mini all"
] ]
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 88 KiB

+23 -7
View File
@@ -2,25 +2,41 @@ import React, { Component } from 'react'
import { Container, Row, Col, Image } from 'react-bootstrap'; import { Container, Row, Col, Image } from 'react-bootstrap';
import './css/AboutUs.css'; import './css/AboutUs.css';
export default class Home extends Component { export default class AboutUs extends Component {
render() { render() {
return ( return (
<Container> <Container>
<Row className="show-Container text-center"> <Row className="show-Container text-center">
<Col xs={12} sm={4} className="person-wrapper"> <Col xs={12} sm={4} className="person-wrapper">
<Image src="assets/person-1.jpg" roundedCircle className="profile-pic"/> <Image src="assets/person-1.jpg" roundedCircle className="profile-pic" />
<h3>Matteo Scorzafava</h3> <h3>Matteo Scorzafava</h3>
<p>That's a crooked tree. We'll send him to Washington. These little son of a guns hide in your brush and you just have to push them out.</p> Wikipedia <br/>
Gestione 15 secondi <br/>
Recommender Search <br/>
Back-end per popolarità <br/>
Recommender similarityArtist <br/>
<p></p>
</Col> </Col>
<Col xs={12} sm={4} className="person-wrapper"> <Col xs={12} sm={4} className="person-wrapper">
<Image src="assets/person-2.jpg" roundedCircle className="profile-pic"/> <Image src="assets/person-2.jpg" roundedCircle className="profile-pic" />
<h3>Simone Ferrari</h3> <h3>Simone Ferrari</h3>
<p>That's a crooked tree. We'll send him to Washington. These little son of a guns hide in your brush and you just have to push them out.</p> <p>Recommender Random<br/>
Recommender Related<br/>
Recommender Similarity Genere<br/>
Box delle informazioni relative al video
</p>
</Col> </Col>
<Col xs={12} sm={4} className="person-wrapper"> <Col xs={12} sm={4} className="person-wrapper">
<Image src="assets/person-3.jpg" roundedCircle className="profile-pic"/> <Image src="assets/person-3.jpg" roundedCircle className="profile-pic" />
<h3>Gregorio Giacchetti</h3> <h3>Gregorio Giacchetti</h3>
<p>That's a crooked tree. We'll send him to Washington. These little son of a guns hide in your brush and you just have to push them out.</p> <p>Recommender Recent<br/>
Recommender fvitali<br/>
Gestione della barra dei recommender<br/>
AboutUs <br/>
Recommender similarityArtist
</p>
</Col> </Col>
</Row> </Row>
</Container> </Container>
+18 -18
View File
@@ -34,19 +34,17 @@ class App extends React.Component {
exact: true, exact: true,
strict: false strict: false
}); });
if (prevState.videoId !== null){
if (idMatch && idMatch.params.id !== prevState.videoId) { if (idMatch && idMatch.params.id !== prevState.videoId) {
localStorage.setItem('prevId', prevState.videoId) localStorage.setItem('prevId', prevState.videoId)
localStorage.setItem('lastId', idMatch.params.id) localStorage.setItem('lastId', idMatch.params.id)
return { videoId: idMatch.params.id }; return { videoId: idMatch.params.id };
} }
else{ else {
if (idMatch && idMatch.params.id !== prevState.videoId) { return null;
localStorage.setItem('lastId', idMatch.params.id)
return { videoId: idMatch.params.id };
} }
return null; }
}}}
render() { render() {
@@ -58,19 +56,21 @@ class App extends React.Component {
<Row className="upperSection"> <Row className="upperSection">
<Col <Col
xs="12" xs="12"
lg={this.state.isInfoToggled ? { span: 6, offset: 0, order: 2 } : { span: 7, offset: 1, order: 2 }} lg={this.state.isInfoToggled ? { span: 8, offset: 0, order: 2 } : { span: 9, offset: 0, order: 2 }}
className="align-content-center pt-2 p-1 pr-2"> className="blackBg align-content-center">
<ReactHeight onHeightReady={height => { this.setState({ playerHeight: height }) }}> <Col xs="12" lg={this.state.isInfoToggled ? { span: 10, offset: 1 } : { span: 10, offset: 1 }}>
<VideoPlayer videoId={this.state.videoId} /> <ReactHeight onHeightReady={height => { this.setState({ playerHeight: height }) }}>
</ReactHeight> <VideoPlayer videoId={this.state.videoId} />
</ReactHeight>
</Col>
</Col> </Col>
<Col <Col
xs="12" xs="12"
lg={this.state.isInfoToggled ? { span: 5, order: 1 } : { span: 3, order: 1 }} lg={this.state.isInfoToggled ? { span: 4, order: 1 } : { span: 3, order: 1 }}
className="p-0 p-xs-0" className={this.state.isInfoToggled ? "" : "blackBg"}
style={window.innerWidth < 992 ? style={window.innerWidth < 992 ?
{ 'overflow': "auto", 'display': 'fixed', 'maxHeight': this.state.playerHeight * 1.4 + 'px' } : { 'overflow': "auto", 'display': 'fixed', 'maxHeight': this.state.playerHeight * 1.4 + 'px' } :
{ 'overflow': "auto", 'maxHeight': `${this.state.playerHeight}px` }}> { 'overflow': "auto", 'maxHeight': `${this.state.playerHeight}px` }}>
{/* true = style for xs ;; false = style for lg */} {/* true = style for xs ;; false = style for lg */}
<div className="d-flex justify-content-center"> <div className="d-flex justify-content-center">
<Button <Button
@@ -89,7 +89,7 @@ class App extends React.Component {
</Collapse> </Collapse>
</Col> </Col>
</Row> </Row>
<Row style={{ "maxHeight": `calc(98vh - ${this.state.playerHeight}px - 46px)` }} className='mt-1 downSection'> <Row style={{ "maxHeight": `calc(98vh - ${this.state.playerHeight}px - 40px)` }} className='mt-2 downSection'>
<Switch> <Switch>
<Route exact path='/' component={VideoList} /> <Route exact path='/' component={VideoList} />
<Route path={"/video/:id"} component={Suggestion} /> <Route path={"/video/:id"} component={Suggestion} />
+16 -13
View File
@@ -1,5 +1,4 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Row } from 'react-bootstrap';
import { Switch, Route } from 'react-router'; import { Switch, Route } from 'react-router';
import Random from './reccomenders/Random'; import Random from './reccomenders/Random';
import Related from './reccomenders/Related'; import Related from './reccomenders/Related';
@@ -7,16 +6,19 @@ import Search from './reccomenders/Search';
import fvitali from './reccomenders/fvitali'; import fvitali from './reccomenders/fvitali';
import popGlobaleAssoluta from './reccomenders/popGlobaleAssoluta'; import popGlobaleAssoluta from './reccomenders/popGlobaleAssoluta';
import Recent from './reccomenders/Recent'; import Recent from './reccomenders/Recent';
import similarityArtist from './reccomenders/similarityArtist';
import popLocaleAss from './reccomenders/popLocaleAss';
import PopRelLoc from './reccomenders/popRelLoc'; import PopRelLoc from './reccomenders/popRelLoc';
import genreSimilarity from './reccomenders/genreSimilarity';
// and so on.. // and so on..
class Suggestion extends Component { class Suggestion extends Component {
// static getDerivedStateFromProps(nextProps, prevState) { // static getDerivedStateFromProps(nextProps, prevState) {
// } // }
// componentDidMount() { // componentDidMount() {
// } // }
@@ -36,18 +38,19 @@ class Suggestion extends Component {
render() { render() {
return ( return (
<React.Fragment> <React.Fragment>
<Row>
<Switch> <Switch>
<Route path='/video/:id/vitali' component={fvitali} /> <Route path='/video/:id/genreSimilarity' component={genreSimilarity} />
<Route path='/search/:query' component={Search} /> <Route path='/video/:id/vitali' component={fvitali} />
<Route path='/video/:id/random' component={Random} /> <Route path='/search/:query' component={Search} />
<Route path='/video/:id/popGlobalAssoluta' component={popGlobaleAssoluta} /> <Route path='/video/:id/random' component={Random} />
<Route path='/video/:id/recent' component={Recent} /> <Route path='/video/:id/popGlobalAssoluta' component={popGlobaleAssoluta} />
<Route path='/video/:id/popRelLoc' component={PopRelLoc} /> <Route path='/video/:id/popLocaleAss' component={popLocaleAss} />
{/* lasciare questo per ultimo */} <Route path='/video/:id/recent' component={Recent} />
<Route component={Related} /> <Route path='/video/:id/similarityArtist' component={similarityArtist} />
<Route path='/video/:id/popRelLoc' component={PopRelLoc} />
{/* lasciare questo per ultimo */}
<Route component={Related} />
</Switch> </Switch>
</Row>
</React.Fragment> </React.Fragment>
); );
} }
+20 -32
View File
@@ -22,15 +22,15 @@ class TopBar extends Component {
} }
handleSubmit = event => { handleSubmit = event => {
event.preventDefault(); event.preventDefault();
event.target.reset(); // event.target.reset();
this.props.history.push('/search/' + this.state.query); this.props.history.push('/search/' + this.state.query);
} }
render() { render() {
return ( return (
<React.Fragment> <React.Fragment>
<Navbar bg="dark" className="py-0 px-1 w-100" expand="md"> <Navbar bg="primary" varian="dark" className="py-0 px-1 w-100" expand="md">
<Navbar.Brand> <Navbar.Brand>
<Link to="/">HazeTV</Link> <Link className="text-light" to="/">HazeTV</Link>
</Navbar.Brand> </Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" /> <Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav"> <Navbar.Collapse id="basic-navbar-nav">
@@ -41,43 +41,31 @@ class TopBar extends Component {
<Link to={{ pathname: `/video/${localStorage['lastId']}/random` }} className="text-light nav-link">Random</Link> <Link to={{ pathname: `/video/${localStorage['lastId']}/random` }} className="text-light nav-link">Random</Link>
<Link to={{ pathname: `/video/${localStorage['lastId']}/related` }} className="text-light nav-link">Related</Link> <Link to={{ pathname: `/video/${localStorage['lastId']}/related` }} className="text-light nav-link">Related</Link>
{<NavDropdown title="Similarity" id="basic-nav-dropdown" > {<NavDropdown title="Similarity" id="basic-nav-dropdown" >
<Link to={{ pathname: `/video/${localStorage['lastId']}/...` }} className="text-dark nav-link"> <Link to={{ pathname: `/video/${localStorage['lastId']}/similarityArtist` }} className="text-light nav-link">
Artist Artist
</Link> </Link>
<Link to={{ pathname: `/video/${localStorage['lastId']}/...` }} className="text-dark nav-link"> <Link to={{ pathname: `/video/${localStorage['lastId']}/genreSimilarity` }} className="text-light nav-link">
Genre Genre
</Link> </Link>
</NavDropdown>} </NavDropdown>}
{<NavDropdown title="Popolarità locale" id="basic-nav-dropdown" > {<NavDropdown title="Popolarità locale" id="basic-nav-dropdown" >
<Link to={{ pathname: `/video/${localStorage['lastId']}/...` }} className="text-dark nav-link">
Assoluta
</Link>
<Link to={{ pathname: `/video/${localStorage['lastId']}/popRelLoc` }} className="text-dark nav-link"> <Link to={{ pathname: `/video/${localStorage['lastId']}/popLocaleAss` }} className="text-light nav-link">
Relativa Assoluta
</Link> </Link>
</NavDropdown>}
{<NavDropdown title="Popolarità globale" id="basic-nav-dropdown" > <Link to={{ pathname: `/video/${localStorage['lastId']}/popRelLoc` }} className="text-light nav-link">
Relativa
<Link to={{ pathname: `/video/${localStorage['lastId']}/popGlobalAssoluta` }} className="text-dark nav-link">
Assoluta
</Link>
<Link to={{ pathname: `/video/${localStorage['lastId']}/...` }} className="text-dark nav-link">
Relativa
</Link> </Link>
</NavDropdown>} </NavDropdown>}
<Link to={{ pathname: `/video/${localStorage['lastId']}/progetti` }} className="text-light nav-link">Progetti</Link> <Link to={{ pathname: `/video/${localStorage['lastId']}/popGlobalAssoluta` }} className="text-light nav-link">popGlobalAssoluta</Link>
</Nav> </Nav>
<form className="form-inline" onSubmit={this.handleSubmit}> <form className="form-inline" onSubmit={this.handleSubmit}>
@@ -87,7 +75,7 @@ class TopBar extends Component {
className="mr-sm-2" className="mr-sm-2"
onChange={this.handleChange} onChange={this.handleChange}
/> />
<Button type="submit" variant="outline-success">Search</Button> <Button type="submit" variant="secondary">Search</Button>
</form> </form>
</Navbar.Collapse> </Navbar.Collapse>
</Navbar> </Navbar>
+10 -4
View File
@@ -50,11 +50,14 @@ class VideoInfo extends React.Component {
comments: commentThreadResponse.data.items, comments: commentThreadResponse.data.items,
isLoaded: true isLoaded: true
}); });
let index = videosResponse.data.items[0].topicDetails.relevantTopicIds.indexOf('/m/04rlf');
videosResponse.data.items[0].topicDetails.relevantTopicIds.splice(index, 1);
localStorage.setItem('lastTopic', videosResponse.data.items[0].topicDetails.relevantTopicIds);
}), error => { }), error => {
console.error(error); console.error(error);
this.setState({ this.setState({
isLoaded: true, error,
error isLoaded: true
}); });
}); });
} }
@@ -64,14 +67,17 @@ class VideoInfo extends React.Component {
const momentDuration = this.momentDuration; const momentDuration = this.momentDuration;
const ISO_8601parse = this.ISO_8601parse; const ISO_8601parse = this.ISO_8601parse;
if (error) { if (error) {
let htmlRegEx = /<\/?\w+((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[\^'">\s]+))?)+\s*|\s*)\/?>/gm;
return ( return (
<span className="text-white"> <span className="text-white">
<span>{}</span> <span>{}</span>
Error: {error.message} -- Cannot get {error.config.url} Error: {error.message} -- {error.response.data && error.response.data.error.message.replace(htmlRegEx, '')}
</span> </span>
); );
} else if (!isLoaded) { } else if (!isLoaded) {
return <span className="text-white">Loading...</span>; return <div className="text-center">
<div className="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
</div>;
} else { } else {
return ( return (
<React.Fragment> <React.Fragment>
+72 -47
View File
@@ -1,48 +1,48 @@
import React from 'react'; import React from 'react';
import { Col, ListGroup, Card, CardDeck } from 'react-bootstrap'; // eslint-disable-line no-unused-vars import { Col, ListGroup, Card, CardDeck, Media } from 'react-bootstrap'; // eslint-disable-line no-unused-vars
import axios from 'axios'; // eslint-disable-line no-unused-vars import axios from 'axios'; // eslint-disable-line no-unused-vars
import { Link } from 'react-router-dom'; // eslint-disable-line no-unused-vars import { Link } from 'react-router-dom'; // eslint-disable-line no-unused-vars
/*eslint no-console: ["error", { allow: ["warn", "error", "info"] }] */ /*eslint no-console: ["error", { allow: ["warn", "error", "info"] }] */
class VideoList extends React.Component { class VideoList extends React.Component {
state = { state = {
error: null, error: null,
isLoaded: false, isLoaded: false,
items: [], items: [],
headers: null headers: null
}; };
componentDidMount() { componentDidMount() {
localStorage['fixedVideoList'] ? localStorage['fixedVideoList'] ?
this.setState({ this.setState({
items: this.shuffleArray(JSON.parse(localStorage.getItem('fixedVideoList'))), items: this.shuffleArray(JSON.parse(localStorage.getItem('fixedVideoList'))),
isLoaded: true isLoaded: true
}) : }) :
axios.get('http://site1825.tw.cs.unibo.it/video.json').then( axios.get('http://site1825.tw.cs.unibo.it/video.json').then(
response => { response => {
this.shuffleArray(response.data); this.shuffleArray(response.data);
this.getThumbnails(response.data) this.getThumbnails(response.data)
.then(videoList => localStorage.setItem('fixedVideoList',JSON.stringify(videoList))); .then(videoList => localStorage.setItem('fixedVideoList', JSON.stringify(videoList)));
}, },
// Note: it's important to handle errors here // Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow // instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components. // exceptions from actual bugs in components.
error => { error => {
console.error(error); console.error(error);
this.setState({ this.setState({
isLoaded: true, isLoaded: true,
error error
}); });
} }
) )
} }
// METODI // METODI
getThumbnails(videoItems) { getThumbnails(videoItems) {
let idToLookFor = videoItems.map(item => item.videoID); let idToLookFor = videoItems.map(item => item.videoID);
let finalChunk = (videoItems.length % 50) + 100; let finalChunk = (videoItems.length % 50) + 100;
return axios return axios
.all([ .all([
// chain 3 parallel get // chain 3 parallel get
axios.get('https://www.googleapis.com/youtube/v3/videos', { axios.get('https://www.googleapis.com/youtube/v3/videos', {
@@ -74,22 +74,24 @@ class VideoList extends React.Component {
]) ])
.then( .then(
axios.spread((res1, res2, res3) => { axios.spread((res1, res2, res3) => {
[...res1.data.items,...res2.data.items,...res3.data.items] [...res1.data.items, ...res2.data.items, ...res3.data.items]
.map(resCurrentValue => .map(resCurrentValue =>
Object.defineProperty( Object.defineProperty(
videoItems.find(videoItem => { videoItems.find(videoItem => {
return videoItem.videoID === resCurrentValue.id; return videoItem.videoID === resCurrentValue.id;
}), }),
'thumbnail', 'thumbnail',
{ value: resCurrentValue.snippet.thumbnails.medium.url, {
enumerable: true } value: resCurrentValue.snippet.thumbnails.medium.url,
) enumerable: true
); }
)
);
this.setState({ this.setState({
isLoaded: true, isLoaded: true,
items: videoItems items: videoItems
}); });
return new Promise((resolve, reject) => resolve(videoItems)) return new Promise((resolve, reject) => resolve(videoItems))
}) })
) )
} }
@@ -113,13 +115,9 @@ class VideoList extends React.Component {
</React.Fragment> </React.Fragment>
); );
} else if (!isLoaded) { } else if (!isLoaded) {
return ( return <Col className="text-center">
<React.Fragment> <div className="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
<Col> </Col>;
<p className="text-center">Loading...</p>
</Col>
</React.Fragment>
);
} else { } else {
return ( return (
<React.Fragment> <React.Fragment>
@@ -152,6 +150,33 @@ class VideoList extends React.Component {
</Col> </Col>
))} ))}
</React.Fragment> </React.Fragment>
// <ul className="list-unstyled">{
// items.map(item => (
// <Link
// key={item.videoId}
// as="li" className="media mx-5 my-1 suggItem"
// to={{
// pathname: '/video/' + item.videoID,
// state: {
// artist: item.artist,
// title: item.title
// }
// }}>
// <img
// width={240}
// height={120}
// className="align-self-center mr-3"
// src={item.thumbnail}
// alt={'Thumbnail of ' + item.title}
// />
// <Media.Body>
// <h5>{item.artist} - {item.title}</h5>
// <p>{item.category}</p>
// </Media.Body>
// </Link>
// ))}</ul>
); );
} }
} }
+84 -88
View File
@@ -6,48 +6,48 @@ import { withRouter } from 'react-router-dom';
class VideoPlayer extends React.Component { class VideoPlayer extends React.Component {
state = { state = {
opt: { opt: {
// height: '390', // height: '390',
// width: '640', // width: '640',
playerVars: { playerVars: {
// https://developers.google.com/youtube/player_parameters // https://developers.google.com/youtube/player_parameters
autoplay: 0, autoplay: 0,
modestbranding: 1, modestbranding: 1,
fs: 0, fs: 0,
iv_load_policy:3, iv_load_policy: 3,
rel: 0, rel: 0,
origin//: 'http://site1854.tw.cs.unibo.it' origin//: 'http://site1854.tw.cs.unibo.it'
} }
}, },
promise1: null, promise1: null,
promise2: null, promise2: Promise,
outsideReject1: null, outsideReject1: null,
outsideReject2: null outsideReject2: null
}; };
render() { render() {
return ( return (
<YouTube <YouTube
opts={this.state.opt} opts={this.state.opt}
videoId={this.props.videoId} videoId={this.props.videoId}
onReady={this._onReady} onReady={this._onReady}
onPlay={this._onPlay} onPlay={this._onPlay}
onError={this._onError} onError={this._onError}
onStateChange={this._onStateChange} onStateChange={this._onStateChange}
className="embed-responsive-item" className="embed-responsive-item"
containerClassName="embed-responsive embed-responsive-16by9" containerClassName="embed-responsive embed-responsive-16by9"
/> />
); );
} }
// access to player in all event handlers via event.target // access to player in all event handlers via event.target
_onError(event) { _onError(event) {
console.error('Player error: ', event); console.error('Player error: ', event);
} }
_onPlay(event) {} _onPlay(event) { }
_onReady(event) {} _onReady(event) { }
_onStateChange(event) { _onStateChange(event) {
let timerId1, timerId2; let timerId1, timerId2;
@@ -78,7 +78,7 @@ class VideoPlayer extends React.Component {
timerId1 = window.setInterval(() => { timerId1 = window.setInterval(() => {
if (event.target.getCurrentTime() > 15) if (event.target.getCurrentTime() > 15)
resolve(event.target.getVideoData().video_id); resolve(event.target.getVideoData().video_id);
}, 1000); }, 500);
this.setState({ outsideReject1: reject }); this.setState({ outsideReject1: reject });
}) })
}); });
@@ -86,83 +86,79 @@ class VideoPlayer extends React.Component {
.then( .then(
videoIdWatched => { videoIdWatched => {
// post request to local db // post request to local db
let reason = new URLSearchParams(this.props.location.search).get("ref");
axios axios
.post('http://site1854.tw.cs.unibo.it/videotrack', { .post('http://site1854.tw.cs.unibo.it/videotrack', {
id: videoIdWatched.toString() id: videoIdWatched.toString(),
reason: reason || undefined,
prevId: localStorage['prevId']
}) })
.then( .then(
response => console.info(response.data), response => console.info(response.data),
error => this.setState({error})//console.error(error, videoIdWatched.toString()) error => this.setState({ error })//console.error(error, videoIdWatched.toString())
); );
// local storage logic for recent reccomender//tmp è il video che devo inserire sulla lista // local storage logic for recent reccomender
let tmp = JSON.parse(localStorage.getItem('lastWatched')) let tmp = JSON.parse(localStorage.getItem('lastWatched'))
let j=tmp.findIndex(el=>el.id.toString()===videoIdWatched.toString()) let j = tmp.findIndex(el => el.id.toString() === videoIdWatched.toString())
if (j<0) { if (j < 0) {
if (localStorage['prevId']==='1') if (localStorage['prevId'] === '1') {
{ //per primo video
//per primo video tmp.push({
tmp.push({ 'id': videoIdWatched.toString(),
'id': videoIdWatched.toString(), 'lastWatched': new Date(),
'lastWatched' : new Date(), 'timesWatched': 1,
'timesWatched' : 1, 'prevVideos': []
'prevVideos': [] })
}) }
} else {
else { //Prima volta video generico
//Prima volta video generico tmp.push({
tmp.push({ 'id': videoIdWatched.toString(),
'id': videoIdWatched.toString(), 'lastWatched': new Date(),
'lastWatched' : new Date(), 'timesWatched': 1,
'timesWatched' : 1, 'prevVideos': [{ id: [localStorage["prevId"]].toString() }]
'prevVideos' : [{id:[localStorage["prevId"]].toString()}] })
}) }
} }
} else {
else { //altre volte
// if (tmp[j].id=='0J2QdDbelmY'){ tmp[j].timesWatched++;
// tmp[j].timesWatched++; tmp[j].lastWatched = new Date();
// tmp[j].lastWatched= new Date(); let z = tmp[j].prevVideos.findIndex(el => el.id === localStorage["prevId"].toString())
// let z=tmp[j].prevVideos.findIndex(el=>el.id === localStorage["prevId"].toString()) if (z < 0) {
// if (z<0){ tmp[j].prevVideos.push({
// tmp[j].prevVideos.push({ 'id': localStorage["prevId"].toString()
// 'id': localStorage["prevId"].toString() })
// })}
// } else {
//altre volte
tmp[j].timesWatched++;
tmp[j].lastWatched= new Date();
let z=tmp[j].prevVideos.findIndex(el=>el.id === localStorage["prevId"].toString())
if (z<0){
tmp[j].prevVideos.push({
'id': localStorage["prevId"].toString()
})}
//}
} }
tmp.sort((a, b) => new Date(b.lastWatched).getTime() - new Date(a.lastWatched).getTime()); }
tmp.sort((a, b) => new Date(b.lastWatched).getTime() - new Date(a.lastWatched).getTime());
localStorage.setItem('lastWatched', JSON.stringify(tmp));
localStorage.setItem('lastWatched', JSON.stringify(tmp));
//.finally(()=>{}) //.finally(()=>{})
clearInterval(timerId1); clearInterval(timerId1);
return new Promise ((resolve,reject) => resolve(videoIdWatched))
}, },
error => console.error(error) error => console.error(error)
) )
.finally((id) => this.setState({ promise1: null, outsideReject1: null }));
.finally(() => this.setState({ promise1: null, outsideReject1: null })); break;
break;
case 5: case 5:
if (this.state.promise1) if (this.state.promise1)
this.state.outsideReject1('Video changed'); this.state.outsideReject1('Video changed');
break; break;
default: default:
break; break;
} }
return new Promise ((resolve,reject) => resolve('ok'))
}) })
.finally(()=>{this.setState({promise2: null, outsideReject2: null})}); .finally((res) => { this.setState({ promise2: null, outsideReject2: null }) });
} }
} }
_onStateChange = this._onStateChange.bind(this); _onStateChange = this._onStateChange.bind(this);
+155 -114
View File
@@ -3,9 +3,10 @@ import { Link } from 'react-router-dom';
import wikijs from 'wikijs'; import wikijs from 'wikijs';
import wdk from 'wikidata-sdk'; import wdk from 'wikidata-sdk';
import axios from 'axios'; import axios from 'axios';
import { Table } from 'react-bootstrap'; import { Table, Col } from 'react-bootstrap';
import moment from 'moment'; import moment from 'moment';
import "./css/Wikipedia.css" import "./css/Wikipedia.css"
/*eslint no-console: ["error", { allow: ["warn", "error"] }] */
class Wikipedia extends Component { class Wikipedia extends Component {
@@ -38,11 +39,91 @@ class Wikipedia extends Component {
case "P2624": // metrolyrics id case "P2624": // metrolyrics id
return (<a target="_blank" rel="noopener noreferrer" href={`http://www.metrolyrics.com/${value}`}>{`${value}`}</a>) return (<a target="_blank" rel="noopener noreferrer" href={`http://www.metrolyrics.com/${value}`}>{`${value}`}</a>)
case "P1651": // youtubeid case "P1651": // youtubeid
return ( <Link to={{pathname:`/video/${value}`}}>{`${value}`}</Link>) return (<Link to={{ pathname: `/video/${value}` }}>{`${value}`}</Link>)
case "P2581": case "P2581":
return (<a target="_blank" rel="noopener noreferrer" href={`https://babelnet.org/synset?word=bn:${value}`}>{`${value}`}</a>) return (<a target="_blank" rel="noopener noreferrer" href={`https://babelnet.org/synset?word=bn:${value}`}>{value}</a>)
case "P1827":
return (<a target="_blank" rel="noopener noreferrer" href={`https://musicbrainz.org/iswc/${value}`}>{value}</a>)
default: default:
return(`${value}`) return (`${value}`);
}
}
getMusicBrainzFromWData = (P175, P435=undefined) => { // P175 = artist P435 = workid
const musicbrainzBaseUrl = "https://musicbrainz.org/ws/2"; // more readable
let artist = this.props.artist || this.props.artist2;
let title = this.props.title || this.props.title2;
if (P175.toLowerCase() === artist.trim().toLowerCase()) {
axios
.get(`https://musicbrainz.org/ws/2/recording`, {
params: {
'query': `${title.trim()} AND artistname:${artist.trim()}`, //AND release:${P361}
'limit': 1,
'fmt': 'json'
}
})
.then(resP175EqArtist => {
axios
.get(`${musicbrainzBaseUrl}/recording/${resP175EqArtist.data.recordings[0].id}`, {
params: {
'inc': 'work-rels+artists+releases',
'fmt': 'json'
}
})
.then(resRecordingQuery => {
let musicbrainzWorkId = resRecordingQuery.data.relations.map(x => x['target-type'] === 'work' ? x.work.id.toString() : "") || P435.toString();
axios.get(`${musicbrainzBaseUrl}/work/${musicbrainzWorkId}`, {
params:{
'inc': 'artist-rels url-rels',
'limit': 1,
'offset': 0,
'fmt': 'json'
}
})
.then(musicbrainzWorkRes => {
this.setState({ musicbrainz: musicbrainzWorkRes.data, isMBLoaded: true })
},
error => console.error(error))
}, err => { console.error(err) })
}
, err => console.error(err))
}
else if (P175.toLowerCase() === title.trim().toLowerCase()) { // video title was title - artist
axios
.get(`https://musicbrainz.org/ws/2/recording`, {
params: {
'query': `"${artist.trim()}" AND artistname:"${title.trim()}"`, //AND release:${P361}
'limit': 1,
'fmt': 'json'
}
})
.then(resP175EqTitle => {
axios
.get(`${musicbrainzBaseUrl}/recording/${resP175EqTitle.data.recordings[0].id}`, {
params: {
'inc': 'work-rels+artists+releases',
'fmt': 'json'
}
})
.then(resRecordingQuery => {
let musicbrainzWorkId = resRecordingQuery.data.relations.map(x => x['target-type'] === 'work' ? x.work.id.toString() : "") || P435.toString();
axios.get(`${musicbrainzBaseUrl}/work/${musicbrainzWorkId}`, {
params:{
'inc': 'artist-rels url-rels',
'limit': 1,
'offset': 0,
'fmt': 'json'
}
})
.then(musicbrainzWorkRes => {
this.setState({ musicbrainz: musicbrainzWorkRes.data, isMBLoaded: true })
},
error => console.error(error))
}, err => { console.error(err) })
}
, err => console.error(err))
} }
} }
@@ -60,44 +141,53 @@ class Wikipedia extends Component {
.map(x => wikidatatmp.claims[x][0].mainsnak.datavalue.value.id) .map(x => wikidatatmp.claims[x][0].mainsnak.datavalue.value.id)
.filter(x => typeof x === 'string'), 'en')) .filter(x => typeof x === 'string'), 'en'))
]) ])
.then(axios.spread((resKey, resQ) => { .then(
this.setState({ axios.spread((resKey, resQ) => {
wikidatakeys: Object.entries(resKey.data.entities), this.setState({
wikidataQ: resQ.data.entities, wikidatakeys: Object.entries(resKey.data.entities),
isWDataLoaded: true wikidataQ: resQ.data.entities,
}) isWDataLoaded: true
})) })
try{
this.getMusicBrainzFromWData(
resQ.data.entities[wikidatatmp.claims.P175[0].mainsnak.datavalue.value.id].labels.en.value,
wikidatatmp.claims.P435[0].mainsnak.datavalue.value
);
}
catch(e){
console.error(e)
this.getMusicBrainzFromWData(
resQ.data.entities[wikidatatmp.claims.P175[0].mainsnak.datavalue.value.id].labels.en.value
);
}
}))
if (wikidatatmp.sitelinks.enwiki) if (wikidatatmp.sitelinks.enwiki)
wikijs() wikijs()
.page(wikidatatmp.sitelinks.enwiki.title) .page(wikidatatmp.sitelinks.enwiki.title)
.then(page => Promise.all([page.fullInfo(), page.summary()])) .then(page => Promise.all([page.fullInfo()]))
.then(wikipediaRes => this.setState({ .then(wikipediaRes => this.setState({
wikipedia: { wikipedia: {
"desc": wikipediaRes[1], // "desc": wikipediaRes[1],
"info": wikipediaRes[0].general "info": wikipediaRes[0].general
}, },
wikipediakeys: Object.keys(wikipediaRes[0].general), wikipediakeys: Object.keys(wikipediaRes[0].general),
isWikiLoaded: true isWikiLoaded: true
})) }))
else { else {
console.info('called here')
this.findWikiPage(); this.findWikiPage();
} }
}, error => console.error(error)) }, error => console.error(error))
} }
findWikiPage = () => { findWikiPage = () => {
let wikiquery = `${'undefined' === typeof this.props.title ? this.props.title2.trim() : this.props.title.trim()} (${'undefined' === typeof this.props.artist ? this.props.artist2.trim() : this.props.artist.trim()} song)`; let wikiquery = `${'undefined' === typeof this.props.title ? ('undefined' === typeof this.props.title2 ? this.props.artist2 : this.props.title2) : this.props.title.trim()} ${'undefined' === typeof this.props.title && 'undefined' === typeof this.props.title2 ? "" : `(${'undefined' === typeof this.props.artist ? this.props.artist2.trim() : this.props.artist.trim()} song)`}`;
console.info(wikiquery)
return wikijs() return wikijs()
.find(wikiquery) .find(wikiquery)
.then(res => { .then(res => {
console.info(res, 'a'); return Promise.all([res.fullInfo()]);
return Promise.all([res.fullInfo(), res.summary()]);
}) // find by props.title (props.artist song) }) // find by props.title (props.artist song)
.then(wikipediaRes => this.setState({ .then(wikipediaRes => this.setState({
wikipedia: { wikipedia: {
"desc": wikipediaRes[1],
"info": wikipediaRes[0].general "info": wikipediaRes[0].general
}, },
wikipediakeys: Object.keys(wikipediaRes[0].general), wikipediakeys: Object.keys(wikipediaRes[0].general),
@@ -106,99 +196,50 @@ class Wikipedia extends Component {
} }
componentDidMount() { componentDidMount() {
const musicbrainzBaseUrl = "https://musicbrainz.org/ws/2"; // more readable let timerId1;
// let timerId1;
axios.get(wdk.getReverseClaims('P1651', this.props.videoId)) // P1651 is youtube_video_id property axios.get(wdk.getReverseClaims('P1651', this.props.videoId)) // P1651 is youtube_video_id property
.then(wikidataP1651Res => { .then(wikidataP1651Res => {
if (wikidataP1651Res.data.results.bindings.length) { // got a match if (wikidataP1651Res.data.results.bindings.length) // got a match
this.getWikidataPage(wdk.simplify.sparqlResults(wikidataP1651Res.data))// set in state wikidata this.getWikidataPage(wdk.simplify.sparqlResults(wikidataP1651Res.data))// set in state wikidata
.then(() => { else { //oof -- no P1651 :(
if (this.state.wikidata.claims.P435) { // p435 == musicbrainzWorkId let title = this.props.title || this.props.title2;
axios.get(`${musicbrainzBaseUrl}/work/${this.state.wikidata.claims.P435[0].mainsnak.datavalue.value}`, { // get musicbrainz info axios.get(wdk.searchEntities(`${title.trim()}`))
params: { .then(wikidataSearchRes => {
'inc': 'artist-rels url-rels', try{
'limit': 15, this.getWikidataPage(wikidataSearchRes.data.search[0].title)
'offset': 0, }
'fmt': 'json' catch(e){
} console.error(e)
}).then(
musicbrainzRes1 =>
this.setState({ musicbrainz: musicbrainzRes1.data, isMBLoaded: true })
);
}
else {//oof P435 not found in wikidata
axios.get(`${musicbrainzBaseUrl}/work`, { // search on mb by wikidata title
params: {
'query': `work:${this.state.wikidata.labels.en.value}`,
'limit': 1,
'offset': 0,
'fmt': 'json'
}
}).then(musicbrainzRes2 => {
axios.get(`${musicbrainzBaseUrl}/work/${musicbrainzRes2.data.works[0].id}`,
{ // get musicbrainz info on track by previous id
params: {
'inc': 'artist-rels url-rels',
'limit': 15,
'fmt': 'json'
}
})
.then(
(musicbrainzWorkRes2) =>
this.setState({ musicbrainz: musicbrainzWorkRes2.data, isMBLoaded: true })
);
});
}
});
}
else { //oof
axios.get(`${musicbrainzBaseUrl}/work`, {
params: {
'query': 'undefined' === typeof this.props.title ? this.props.title2 : this.props.title,
'limit': 1,
'offset': 0,
'fmt': 'json'
} }
}).then(musicbrainzRes => {
axios.all([
axios.get(wdk.getReverseClaims('P435', musicbrainzRes.data.works[0].id)), // P435 is musicbrainz id
axios.get(`${musicbrainzBaseUrl}/work/${musicbrainzRes.data.works[0].id}`, { // get musicbrainz info
params: {
'inc': 'artist-rels url-rels',
'limit': 15,
'fmt': 'json'
}
})
]).then(
axios.spread((wikidataP435Res, musicbrainzWorkRes) => {
this.setState({
musicbrainz: musicbrainzWorkRes.data,
isMBLoaded: true
});
if (wikidataP435Res.data.results.bindings.length) // found a match
this.getWikidataPage(wdk.simplify.sparqlResults(wikidataP435Res.data)); // set the result in the state
else if (musicbrainzWorkRes.data.relations.find(currentRel => currentRel.type === "wikidata")) { // found wikidata on music brainz
let wikidataUrl = musicbrainzWorkRes.data.relations.find(currentRel => currentRel.type === "wikidata");
let wikidataEntityRegEx = new RegExp("[^/]+$"); // match last part of url https://regex101.com/r/0wCQHY/1
this.getWikidataPage(wikidataEntityRegEx.exec(wikidataUrl.url.resource)) //.then(() => this.setState({ isWikiLoaded: true }));
}
else { // oof
this.findWikiPage()
}
})
)
}) })
} }
return new Promise((resolve, reject) => {
}).finally(() => { timerId1 = window.setInterval(() => {
// let tmp = JSON.parse(localStorage['currentVideoInfo']); if (this.state.wikidataQ == undefined || this.state.wikidata == undefined) { }
// tmp.id = this.props.videoId; else{
// tmp.artist = this.state.wikipedia.info.artist; try {
// tmp.genre = this.state.wikidataQ[this.state.wikidata.claims["P136"][0].mainsnak.datavalue.value.id].labels.en.value; resolve({
// console.info(tmp) artist: this.state.wikidataQ[this.state.wikidata.claims["P175"][0].mainsnak.datavalue.value.id].labels.en.value.toString(),
// // localStorage.setItem('currentVideoInfo',JSON.stringify(tmp)); genre: this.state.wikidataQ[this.state.wikidata.claims["P136"][0].mainsnak.datavalue.value.id].labels.en.value.toString()
this.setState({ isLoaded: true }) })
}); }
catch{
resolve({ artist: null, genre: null })
}
}
}, 1000)
})
}).then((res) => {
let tmp = JSON.parse(localStorage['currentVideoInfo']);
window.clearInterval(timerId1);
tmp = {
id: this.props.videoId.toString(),
artist: res.artist,
genre: res.genre
}
localStorage.setItem('currentVideoInfo', JSON.stringify(tmp));
})
.finally(() => this.setState({ isLoaded: true }));
} }
@@ -220,11 +261,12 @@ class Wikipedia extends Component {
if (error) { if (error) {
return <React.Fragment>Error: {error.message}</React.Fragment>; return <React.Fragment>Error: {error.message}</React.Fragment>;
} else if (!this.state.isLoaded) { } else if (!this.state.isLoaded) {
return <React.Fragment>Loading...</React.Fragment>; return <Col className="text-center">
<div className="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
</Col>;
} else { } else {
return (<> return (
{/* {isWikiLoaded && wikipedia.desc} */} <Table size="sm">
<Table bordered variant="dark" hover size="sm">
<tbody> <tbody>
{isWikiLoaded && {isWikiLoaded &&
this.state.wikipediakeys.map(key => ( this.state.wikipediakeys.map(key => (
@@ -241,9 +283,9 @@ class Wikipedia extends Component {
${key[1].labels.en ? key[1].labels.en.value : ""} `} ${key[1].labels.en ? key[1].labels.en.value : ""} `}
</td> </td>
<td>{ <td>{
typeof wikidata.claims[key[0]][0].mainsnak.datavalue.value === "string" ? typeof wikidata.claims[key[0]][0].mainsnak.datavalue.value === "string" ? // wikidata external
this.getExternalLink(key[0],wikidata.claims[key[0]][0].mainsnak.datavalue.value) : this.getExternalLink(key[0], wikidata.claims[key[0]][0].mainsnak.datavalue.value) :
typeof wikidata.claims[key[0]][0].mainsnak.datavalue.value.id === "string" ? typeof wikidata.claims[key[0]][0].mainsnak.datavalue.value.id === "string" ? // wikidata Q
<><a target="_blank" rel="noopener noreferrer" href={`https://www.wikidata.org/wiki/${wikidata.claims[key[0]][0].mainsnak.datavalue.value.id}`}> <><a target="_blank" rel="noopener noreferrer" href={`https://www.wikidata.org/wiki/${wikidata.claims[key[0]][0].mainsnak.datavalue.value.id}`}>
{`${this.state.wikidataQ[wikidata.claims[key[0]][0].mainsnak.datavalue.value.id].labels.en ? {`${this.state.wikidataQ[wikidata.claims[key[0]][0].mainsnak.datavalue.value.id].labels.en ?
this.state.wikidataQ[wikidata.claims[key[0]][0].mainsnak.datavalue.value.id].labels.en.value : this.state.wikidataQ[wikidata.claims[key[0]][0].mainsnak.datavalue.value.id].labels.en.value :
@@ -274,7 +316,6 @@ class Wikipedia extends Component {
} }
</tbody> </tbody>
</Table> </Table>
</>
); );
} }
} }
+74 -2
View File
@@ -1,9 +1,81 @@
.upperSection{ .upperSection{
background-color:#000000;
display: fixed; display: fixed;
} }
.blackBg{
background-color:#000000;
}
.downSection{ .downSection{
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
} }
.suggItem:hover{
background-color: #213241;
text-decoration: none;
/* color: */
}
.suggItem:hover h5{
color: #DF691A;
}
.suggItem p{
color: #FFFFFF;
}
/* LOADING ANIMATION */
.lds-ellipsis {
display: inline-block;
position: relative;
width: 64px;
height: 64px;
}
.lds-ellipsis div {
position: absolute;
top: 27px;
width: 11px;
height: 11px;
border-radius: 50%;
background: #fff;
animation-timing-function: cubic-bezier(0, 1, 1, 0);
}
.lds-ellipsis div:nth-child(1) {
left: 6px;
animation: lds-ellipsis1 0.6s infinite;
}
.lds-ellipsis div:nth-child(2) {
left: 6px;
animation: lds-ellipsis2 0.6s infinite;
}
.lds-ellipsis div:nth-child(3) {
left: 26px;
animation: lds-ellipsis2 0.6s infinite;
}
.lds-ellipsis div:nth-child(4) {
left: 45px;
animation: lds-ellipsis3 0.6s infinite;
}
@keyframes lds-ellipsis1 {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
@keyframes lds-ellipsis3 {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
}
}
@keyframes lds-ellipsis2 {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(19px, 0);
}
}
+4 -1
View File
@@ -1,5 +1,8 @@
a[target=_blank]{ a[target=_blank]{
background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAXklEQVQoka2QwQ3AMAwCs1N28k7eiZ3oI7IcU6efBomXOREyxhUZ2brTdNAcVB2BaJgCVcDAalJLXsB+iLAjm1pAwzHWHD3gWMcMg/ERMjKfFOHVqMEGqEM/gKP/6gE2f+h+Z5P45wAAAABJRU5ErkJggg==') center right no-repeat; background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAXklEQVQoka2QwQ3AMAwCs1N28k7eiZ3oI7IcU6efBomXOREyxhUZ2brTdNAcVB2BaJgCVcDAalJLXsB+iLAjm1pAwzHWHD3gWMcMg/ERMjKfFOHVqMEGqEM/gKP/6gE2f+h+Z5P45wAAAABJRU5ErkJggg==') center right no-repeat;
padding-right: 14px; padding-right: 14px;
}
tbody a{
color: #DF691A !important;
} }
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+13 -10
View File
@@ -1,21 +1,24 @@
import 'bootstrap/dist/css/bootstrap.css'; // import 'bootstrap/dist/css/bootstrap.css';
// import './css/bootswatch/darkly/bootstrap.min.css'
import './css/bootswatch/superhero/bootstrap.min.css'
import 'react-app-polyfill/ie9'; import 'react-app-polyfill/ie9';
//import '@babel/polyfill'; //import '@babel/polyfill';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import App from './App'; import App from './App';
import registerServiceWorker from './registerServiceWorker'; import registerServiceWorker from './registerServiceWorker';
import { HashRouter, BrowserRouter } from 'react-router-dom'; // eslint-disable-line import { HashRouter, BrowserRouter } from 'react-router-dom'; // eslint-disable-line
localStorage['lastWatched'] ? function (){}() : localStorage.setItem('lastWatched', JSON.stringify([])) localStorage['lastWatched'] ? function () { }() : localStorage.setItem('lastWatched', JSON.stringify([]))
localStorage['first'] ? function (){}() : localStorage.setItem('first', JSON.stringify([])) localStorage['first'] ? function () { }() : localStorage.setItem('first', JSON.stringify([]))
localStorage['lastId'] ? function (){}() : localStorage.setItem('lastId','0J2QdDbelmY') localStorage['prevId'] ? function () { }() : localStorage.setItem('prevId', '1')
localStorage['prevId'] ? function (){}() : localStorage.setItem('prevId','1') localStorage['lastId'] ? function () { }() : localStorage.setItem('lastId', '0J2QdDbelmY')
localStorage['currentVideoInfo']? function(){}() : localStorage.setItem('currentVideoInfo', JSON.stringify({ localStorage['currentVideoInfo'] ? function () { }() : localStorage.setItem('currentVideoInfo', JSON.stringify({
'id':'0J2QdDbelmY', 'id': '0J2QdDbelmY',
'artist':'Seven Nation Army', 'artist': 'Seven Nation Army',
'genre':'Rock' 'genre': 'Rock'
})); }));
localStorage['lastTopic'] ? function () { }() : localStorage.setItem('lastTopic', '/m/04rlf');
ReactDOM.render(<BrowserRouter><App /></BrowserRouter>, document.getElementById('root')); ReactDOM.render(<BrowserRouter><App /></BrowserRouter>, document.getElementById('root'));
registerServiceWorker(); registerServiceWorker();
+68 -38
View File
@@ -1,20 +1,20 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import axios from "axios"; import axios from "axios";
import createpageToken from 'youtube-page-token'; import createpageToken from 'youtube-page-token';
import {Card,Col} from "react-bootstrap"; import { Card, Col, Media } from "react-bootstrap";
import {Link} from "react-router-dom"; import { Link } from "react-router-dom";
import moment from "moment"; import moment from "moment";
class RecommenderRandom extends Component{ class RecommenderRandom extends Component {
state={ state = {
items : [], items: [],
} }
ISO_8601parse(a) { ISO_8601parse(a) {
return moment(a, moment.ISO_8601).format('ddd, DD/MM/YYYY hh:mm:ss'); return moment(a, moment.ISO_8601).format('ddd, DD/MM/YYYY hh:mm:ss');
} }
componentDidMount(){ componentDidMount() {
let pos = Math.floor(Math.random() * 500); let pos = Math.floor(Math.random() * 500);
let pageToken = createpageToken(pos); /* createPageToken(pos) restituisce il pageToken relativo al numero let pageToken = createpageToken(pos); /* createPageToken(pos) restituisce il pageToken relativo al numero
di pagina che gli viene passato(in questo caso pos) che verrai poi usato di pagina che gli viene passato(in questo caso pos) che verrai poi usato
@@ -23,16 +23,16 @@ class RecommenderRandom extends Component{
axios.get("https://www.googleapis.com/youtube/v3/search", { axios.get("https://www.googleapis.com/youtube/v3/search", {
params: { params: {
'part': 'snippet', 'part': 'snippet',
'topicId' : '/m/04rlf', //Filtro per la musica.In questo caso il valore è quello della parent directory 'topicId': '/m/04rlf', //Filtro per la musica.In questo caso il valore è quello della parent directory
'type' : 'video', //che contiene tutti i generi di musica 'type': 'video', //che contiene tutti i generi di musica
'maxResults': '21', 'maxResults': '21',
'pageToken' : pageToken, 'pageToken': pageToken,
'key': 'AIzaSyA21Odw9UdfiOJnxvA_eDfOW0OxuUfHKig' 'key': 'AIzaSyA21Odw9UdfiOJnxvA_eDfOW0OxuUfHKig'
} }
}).then( }).then(
response => { response => {
this.setState({ this.setState({
items : response.data.items, items: response.data.items,
}) })
}, },
error => { error => {
@@ -41,34 +41,64 @@ class RecommenderRandom extends Component{
) )
} }
render(){ render() {
const ISO_8601parse = this.ISO_8601parse; const ISO_8601parse = this.ISO_8601parse;
if (window.innerWidth < 992) {
return (<React.Fragment> return (<React.Fragment>
{this.state.items.map(function (item,index) { {this.state.items.map(function (item, index) {
return( return (
<Col key={index} md='4'> <Col key={index} md='4'>
<Card> <Card>
<Link <Link
to={{ to={{
pathname: '/video/' + item.id.videoId, pathname: '/video/' + item.id.videoId,
}} search: "?ref=Random"
> }}
<Card.Img >
src={item.snippet.thumbnails.high.url} <Card.Img
alt={'Thumbnail of ' + item.snippet.title} src={item.snippet.thumbnails.high.url}
/> alt={'Thumbnail of ' + item.snippet.title}
<Card.ImgOverlay> />
<Card.Title className="bg-dark d-inline text-white"> <Card.ImgOverlay>
{item.snippet.title}<br/> <Card.Title className="bg-dark d-inline text-white">
{item.snippet.channelTitle} - {ISO_8601parse(item.snippet.publishedAt)} {item.snippet.title}<br />
</Card.Title> {item.snippet.channelTitle} - {ISO_8601parse(item.snippet.publishedAt)}<br />
</Card.ImgOverlay> <span>Reccomender: Random</span>
</Link> </Card.Title>
</Card> </Card.ImgOverlay>
</Col> </Link>
) </Card>
})} </Col>
</React.Fragment> ); )
} })}
</React.Fragment>);
}else{
return (
<ul className="list-unstyled">{
this.state.items.map(item => (
<Link
key={item.id.videoId}
as="li" className="media mx-5 my-1 suggItem"
to={{
pathname: `/video/${item.id.videoId}`,
search: '?ref=Random'
}}>
<img
width={240}
height={120}
className="align-self-center mr-3 img-fluid"
src={item.snippet.thumbnails.high.url}
alt={'Thumbnail of ' + item.snippet.title}
/>
<Media.Body>
<h5>{item.snippet.title}</h5>
<p> {item.snippet.channelTitle} - {ISO_8601parse(item.snippet.publishedAt)}</p>
<p>Reccomender: Random</p>
</Media.Body>
</Link>
))
}</ul>
);
}}
} }
export default RecommenderRandom; export default RecommenderRandom;
+55 -30
View File
@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Col, ListGroup, Card, CardDeck } from 'react-bootstrap'; // eslint-disable-line no-unused-vars import { Col, Card, Media } from 'react-bootstrap'; // eslint-disable-line no-unused-vars
import axios from 'axios'; // eslint-disable-line no-unused-vars import axios from 'axios'; // eslint-disable-line no-unused-vars
import { Link } from 'react-router-dom'; // eslint-disable-line no-unused-vars import { Link } from 'react-router-dom'; // eslint-disable-line no-unused-vars
/*eslint no-console: ["error", { allow: ["warn", "error", "info"] }] */ /*eslint no-console: ["error", { allow: ["warn", "error", "info"] }] */
@@ -55,7 +55,6 @@ class Recent extends React.Component {
} }
render() { render() {
const { error, isLoaded, items } = this.state; const { error, isLoaded, items } = this.state;
if (error) { if (error) {
@@ -65,41 +64,67 @@ class Recent extends React.Component {
</React.Fragment> </React.Fragment>
); );
} else if (!isLoaded) { } else if (!isLoaded) {
return ( return <Col className="text-center">
<React.Fragment> <div className="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
<Col> </Col>;
<p className="text-center">Loading...</p>
</Col>
</React.Fragment>
);
} else { } else {
if (window.innerWidth < 992) {
return ( return (
<React.Fragment> <React.Fragment>
{items.map(item => ( {items.map(item => (
<Col md="4"> <Col key={item.id} md="4">
<Card key={item.id}> <Card >
<Link <Link
to={{ to={{
pathname: '/video/' + item.id, pathname: '/video/' + item.id,
}} search: "?ref=Recent"
> }}
<Card.Img >
src={item.thumbnail} <Card.Img
alt={'Thumbnail of ' + item.name} src={item.thumbnail}
/> alt={'Thumbnail of ' + item.name}
<Card.ImgOverlay> />
<Card.Title className="bg-dark d-inline text-white"> <Card.ImgOverlay>
{item.name}<br /> <Card.Title className="bg-dark d-inline text-white">
{item.lastSelected} {item.name}<br />
</Card.Title> <span>Ultima volta visto: {this.ISO_8601parse(item.lastWatched)} </span><br />
</Card.ImgOverlay> <span>Reccomender: Recent</span>
</Link> </Card.Title>
</Card.ImgOverlay>
</Link>
</Card> </Card>
</Col>))} </Col>))}
</React.Fragment> </React.Fragment>
); );
} }else{
return (
<ul className="list-unstyled">{
this.state.items.map(item => (
<Link
key={item.name}
as="li" className="media mx-5 my-1 suggItem"
to={{
pathname: `/video/${item.name}`,
search: '?ref=Recent'
}}>
<img
width={240}
height={120}
className="align-self-center mr-3 img-fluid"
src={item.thumbnail}
alt={'Thumbnail of ' + item.name}
/>
<Media.Body>
<h5>{item.name}</h5><br />
<p>Ultima volta visto: {this.ISO_8601parse(item.lastWatched)} </p><br />
<p>Reccomender: Recent</p>
</Media.Body>
</Link>
))
}</ul>
);
}}
} }
} }
+53 -23
View File
@@ -1,15 +1,15 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import axios from 'axios'; import axios from 'axios';
import {Link} from "react-router-dom"; import { Link } from "react-router-dom";
import {Card,Col} from "react-bootstrap"; import { Card, Col, Media } from "react-bootstrap";
import moment from "moment"; import moment from "moment";
class Related extends Component { class Related extends Component {
state={ state = {
videoId : localStorage['lastId'], videoId: localStorage['lastId'],
items : [] items: []
} }
ISO_8601parse(a) { ISO_8601parse(a) {
@@ -17,12 +17,12 @@ class Related extends Component {
} }
componentDidMount() { componentDidMount() {
axios.get("https://www.googleapis.com/youtube/v3/search",{ axios.get("https://www.googleapis.com/youtube/v3/search", {
params:{ params: {
'part': 'snippet', 'part': 'snippet',
'relatedToVideoId' : this.props.match.params.id, 'relatedToVideoId': this.props.match.params.id,
'type' : 'video', 'type': 'video',
'maxResults' : '22', 'maxResults': '22',
'key': 'AIzaSyA21Odw9UdfiOJnxvA_eDfOW0OxuUfHKig' 'key': 'AIzaSyA21Odw9UdfiOJnxvA_eDfOW0OxuUfHKig'
} }
}).then( }).then(
@@ -37,14 +37,14 @@ class Related extends Component {
) )
} }
componentDidUpdate(prevProps,prevState){ componentDidUpdate(prevProps, prevState) {
if (prevProps.match.params.id !== this.props.match.params.id){ if (prevProps.match.params.id !== this.props.match.params.id) {
axios.get("https://www.googleapis.com/youtube/v3/search",{ axios.get("https://www.googleapis.com/youtube/v3/search", {
params:{ params: {
'part': 'snippet', 'part': 'snippet',
'relatedToVideoId' : this.props.match.params.id, 'relatedToVideoId': this.props.match.params.id,
'type' : 'video', 'type': 'video',
'maxResults' : '22', 'maxResults': '22',
'key': 'AIzaSyA21Odw9UdfiOJnxvA_eDfOW0OxuUfHKig' 'key': 'AIzaSyA21Odw9UdfiOJnxvA_eDfOW0OxuUfHKig'
} }
}).then( }).then(
@@ -62,15 +62,17 @@ class Related extends Component {
render() { render() {
const ISO_8601parse = this.ISO_8601parse; const ISO_8601parse = this.ISO_8601parse;
if (window.innerWidth < 992) {
return ( return (
<React.Fragment> <React.Fragment>
{this.state.items.map(function (item,index) { {this.state.items.map(function (item, index) {
return( return (
<Col key={index} md="4"> <Col key={index} md="4">
<Card> <Card>
<Link <Link
to={{ to={{
pathname: '/video/' + item.id.videoId, pathname: '/video/' + item.id.videoId,
search: "?ref=Related"
}} }}
> >
<Card.Img <Card.Img
@@ -79,8 +81,9 @@ class Related extends Component {
/> />
<Card.ImgOverlay> <Card.ImgOverlay>
<Card.Title className="bg-dark d-inline text-white"> <Card.Title className="bg-dark d-inline text-white">
{item.snippet.title}<br/> {item.snippet.title}<br />
{item.snippet.channelTitle} - {ISO_8601parse(item.snippet.publishedAt)} {item.snippet.channelTitle} - {ISO_8601parse(item.snippet.publishedAt)}<br />
<span>Reccomender: Related</span>
</Card.Title> </Card.Title>
</Card.ImgOverlay> </Card.ImgOverlay>
</Link> </Link>
@@ -90,7 +93,34 @@ class Related extends Component {
})} })}
</React.Fragment> </React.Fragment>
); );
} }else{
return (
<ul className="list-unstyled">{
this.state.items.map(item => (
<Link
key={item.id.videoId}
as="li" className="media mx-5 my-1 suggItem"
to={{
pathname: `/video/${item.id.videoId}`,
search: '?ref=Related'
}}>
<img
width={240}
height={120}
className="align-self-center mr-3 img-fluid"
src={item.snippet.thumbnails.high.url}
alt={'Thumbnail of ' + item.snippet.title}
/>
<Media.Body>
<h5>{item.snippet.title}</h5>
<p> {item.snippet.channelTitle} - {ISO_8601parse(item.snippet.publishedAt)}</p>
<p>Reccomender: Related</p>
</Media.Body>
</Link>
))
}</ul>
);
}}
} }
Related.propTypes = { Related.propTypes = {
+45 -10
View File
@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import axios from 'axios'; import axios from 'axios';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Card, Col } from 'react-bootstrap'; import { Col, Media, Card } from 'react-bootstrap';
class Search extends React.Component { class Search extends React.Component {
@@ -44,7 +44,8 @@ class Search extends React.Component {
// } // }
componentDidUpdate(prevProps, prevState) { componentDidUpdate(prevProps, prevState) {
if (prevProps.match.params.query !== this.props.match.params.query) if (prevProps.match.params.query !== this.props.match.params.query) {
this.setState({ isLoaded: false })
if (new RegExp("[0-9A-Za-z_-]{10}[048AEIMQUYcgkosw]").test(this.props.match.params.query.toString())) if (new RegExp("[0-9A-Za-z_-]{10}[048AEIMQUYcgkosw]").test(this.props.match.params.query.toString()))
axios.get('https://www.googleapis.com/youtube/v3/videos', { axios.get('https://www.googleapis.com/youtube/v3/videos', {
params: { params: {
@@ -68,6 +69,8 @@ class Search extends React.Component {
) )
else else
this.youtubeSearch(); this.youtubeSearch();
}
} }
// componentWillUnmount() { // componentWillUnmount() {
@@ -86,7 +89,7 @@ class Search extends React.Component {
'key': 'AIzaSyA21Odw9UdfiOJnxvA_eDfOW0OxuUfHKig' 'key': 'AIzaSyA21Odw9UdfiOJnxvA_eDfOW0OxuUfHKig'
} }
}).then( }).then(
res => res =>
this.setState({ this.setState({
res: res.data, res: res.data,
isLoaded: true isLoaded: true
@@ -102,15 +105,46 @@ class Search extends React.Component {
if (this.state.error) { if (this.state.error) {
return <React.Fragment>{this.state.error.message} -- {this.state.error.response.data.error.errors[0].reason} </React.Fragment>; return <React.Fragment>{this.state.error.message} -- {this.state.error.response.data.error.errors[0].reason} </React.Fragment>;
} else if (!this.state.isLoaded) { } else if (!this.state.isLoaded) {
return <React.Fragment>Loading...</React.Fragment>; return <Col className="text-center">
<div className="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
</Col>;
} else { } else {
if (window.innerWidth > 992) {
return (
<ul className="list-unstyled">{
this.state.res.items.map(item => (
<Link
key={item.id.videoId}
as="li" className="media mx-5 my-1 suggItem"
to={{
pathname: '/video/' + item.id.videoId,
search: "?ref=Search"
}}>
<img
width={240}
height={120}
className="align-self-center mr-3"
src={item.snippet.thumbnails.medium.url}
alt={'Thumbnail of ' + item.snippet.title}
/>
<Media.Body>
<h5>{item.snippet.title}</h5>
<p>{item.snippet.description}</p>
<p>Reccomender: Search</p>
</Media.Body>
</Link>
))
}</ul>
);
}else{
return ( return (
<React.Fragment> <React.Fragment>
{this.state.res.items.map(item => (<Col key={item.id.videoId} md="4"> {this.state.res.items.map(item => (<Col md="4">
<Card> <Card key={item.id.videoId}>
<Link <Link
to={{ to={{
pathname: '/video/' + item.id.videoId, pathname: '/video/' + item.id.videoId,
search: "?ref=Search"
}} }}
> >
<Card.Img <Card.Img
@@ -119,16 +153,17 @@ class Search extends React.Component {
/> />
<Card.ImgOverlay> <Card.ImgOverlay>
<Card.Title className="bg-dark d-inline text-white"> <Card.Title className="bg-dark d-inline text-white">
{item.snippet.title} <p>{item.snippet.title}</p>
<p className='small'>{item.lastSelected}</p>
<p className='small'>Reccomender: Search</p>
</Card.Title> </Card.Title>
</Card.ImgOverlay> </Card.ImgOverlay>
</Link> </Link>
</Card> </Card>
</Col> </Col>))}
))}
</React.Fragment> </React.Fragment>
); );
} }}
} }
} }
+84 -57
View File
@@ -1,25 +1,24 @@
import React from 'react'; import React from 'react';
import { Col, Card } from 'react-bootstrap'; // eslint-disable-line no-unused-vars import { Col, Card, Media } from 'react-bootstrap'; // eslint-disable-line no-unused-vars
import axios from 'axios'; // eslint-disable-line no-unused-vars import axios from 'axios'; // eslint-disable-line no-unused-vars
import { Link } from 'react-router-dom'; // eslint-disable-line no-unused-vars import { Link } from 'react-router-dom'; // eslint-disable-line no-unused-vars
/*eslint no-console: ["error", { allow: ["warn", "error", "info"] }] */ /*eslint no-console: ["error", { allow: ["warn", "error", "info"] }] */
class fvitali extends React.Component { class fvitali extends React.Component {
state = { state = {
error: null, error: null,
isLoaded: false, isLoaded: false,
items: [], items: [],
}; };
componentDidMount() { componentDidMount() {
axios.get('http://site1825.tw.cs.unibo.it/TW/globpop', { axios.get('http://site1825.tw.cs.unibo.it/TW/globpop', {
'params':{ 'params': {
'id':this.props.match.params.id //se 'id'=>'Id', allora tutti video random, sennò anche per genere simile 'id': this.props.match.params.id //se 'id'=>'Id', allora tutti video random, sennò anche per genere simile
} }
}).then( }).then(
response => { response => {
this.getThumbnailsNames(response.data.recommended); this.getThumbnailsNames(response.data.recommended);
}, },
error => { error => {
@@ -32,33 +31,33 @@ class fvitali extends React.Component {
); );
} }
// METODI // METODI
getThumbnailsNames(reccomended) { getThumbnailsNames(reccomended) {
let idToLookFor = reccomended.map(item => item.videoID); let idToLookFor = reccomended.map(item => item.videoID);
axios.get('https://www.googleapis.com/youtube/v3/videos', { axios.get('https://www.googleapis.com/youtube/v3/videos', {
params: { params: {
part: 'snippet', part: 'snippet',
id: idToLookFor.toString(), id: idToLookFor.toString(),
key: 'AIzaSyA21Odw9UdfiOJnxvA_eDfOW0OxuUfHKig', key: 'AIzaSyA21Odw9UdfiOJnxvA_eDfOW0OxuUfHKig',
fields: 'items(id,snippet/thumbnails/medium,snippet/title)' fields: 'items(id,snippet/thumbnails/medium,snippet/title)'
} }
}) })
.then( .then(
res1 => { res1 => {
res1.data.items.map(resCurrentValue => res1.data.items.map(resCurrentValue =>
Object.defineProperties( Object.defineProperties(
reccomended.find(videoItem => { reccomended.find(videoItem => {
return videoItem.videoID === resCurrentValue.id; return videoItem.videoID === resCurrentValue.id;
}), }),
{ {
'thumbnail': 'thumbnail':
{ value: resCurrentValue.snippet.thumbnails.medium.url }, { value: resCurrentValue.snippet.thumbnails.medium.url },
'name': 'name':
{value: resCurrentValue.snippet.title} { value: resCurrentValue.snippet.title }
}) })
); );
this.setState({ this.setState({
isLoaded: true, isLoaded: true,
@@ -79,38 +78,66 @@ class fvitali extends React.Component {
); );
} else if (!isLoaded) { } else if (!isLoaded) {
return ( return (
<React.Fragment> <Col className="text-center">
<Col> <div className="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
<p className="text-center">Loading...</p> </Col>
</Col>
</React.Fragment>
); );
} else { } else {
return ( if (window.innerWidth < 992) {
<React.Fragment> return (
{items.map(item=>( <Col md="4"> <React.Fragment>
<Card key={item.videoID}> {items.map(item => (<Col md="4">
<Link <Card key={item.videoID}>
to={{ <Link
pathname: '/video/' + item.videoID, to={{
}} pathname: '/video/' + item.videoID,
> search: "?ref=fvitali"
<Card.Img }}
src={item.thumbnail} >
alt={'Thumbnail of ' + item.name} <Card.Img
/> src={item.thumbnail}
<Card.ImgOverlay> alt={'Thumbnail of ' + item.name}
<Card.Title className="bg-dark d-inline text-white"> />
{item.name}<br/> <Card.ImgOverlay>
{item.lastSelected} <Card.Title className="bg-dark d-inline text-white">
</Card.Title> <span>{item.name}</span> <br/>
</Card.ImgOverlay>
</Link> <span>Reccomender: fvitali</span>
</Card> </Card.Title>
</Col>))} </Card.ImgOverlay>
</Link>
</React.Fragment> </Card>
); </Col>))}
</React.Fragment>
);
} else {
return (
<ul className="list-unstyled">{
items.map(item => (
<Link
key={item.videoID}
as="li" className="media mx-5 my-1 suggItem"
to={{
pathname: `/video/${item.videoID}`,
search: '?ref=fvitali'
}}>
<img
width={240}
height={120}
className="align-self-center mr-3 img-fluid"
src={item.thumbnail}
alt={'Thumbnail of ' + item.name}
/>
<Media.Body>
<h5>{item.name}</h5>
<p>Reccomender: fvitali</p>
</Media.Body>
</Link>
))
}</ul>
);
}
} }
} }
} }
+104
View File
@@ -0,0 +1,104 @@
import React, { Component } from 'react';
import axios from 'axios';
import { Card, Col, Media } from "react-bootstrap";
import createpageToken from 'youtube-page-token';
import { Link } from "react-router-dom";
import moment from "moment";
class genreSimilarity extends Component {
state = {
items: []
};
ISO_8601parse(a) {
return moment(a, moment.ISO_8601).format('ddd, DD/MM/YYYY hh:mm:ss');
}
componentDidMount() {
let pos = Math.floor(Math.random() * 100);
let pageToken = createpageToken(pos);
axios.get("https://www.googleapis.com/youtube/v3/search", {
params: {
'part': 'snippet',
'topicId': localStorage['lastTopic'],
'type': 'video',
'maxResults': '21',
'order': 'viewCount',
'pageToken': pageToken,
'key': 'AIzaSyA21Odw9UdfiOJnxvA_eDfOW0OxuUfHKig'
}
}).then(
response => {
this.setState({
items: response.data.items,
})
},
error => {
console.error(error);
}
)
}
render() {
const ISO_8601parse = this.ISO_8601parse;
if (window.innerWidth < 992){
return (<React.Fragment>
{this.state.items.map(function (item, index) {
return (
<Col key={index} md='4'>
<Card>
<Link
to={{
pathname: '/video/' + item.id.videoId,
search: "?ref=genreSimilarity"
}}
>
<Card.Img
src={item.snippet.thumbnails.high.url}
alt={'Thumbnail of ' + item.snippet.title}
/>
<Card.ImgOverlay>
<Card.Title className="bg-dark d-inline text-white">
{item.snippet.title}<br />
{item.snippet.channelTitle}<br />
<span>Reccomender: similarityArtist</span>
</Card.Title>
</Card.ImgOverlay>
</Link>
</Card>
</Col>
)
})}
</React.Fragment>);
}else {
return (
<ul className="list-unstyled">{
this.state.items.map(item => (
<Link
key={item.id.videoId}
as="li" className="media mx-5 my-1 suggItem"
to={{
pathname: `/video/${item.id.videoId}`,
search: '?ref=genreSimilarity'
}}>
<img
width={240}
height={120}
className="align-self-center mr-3 img-fluid"
src={item.snippet.thumbnails.high.url}
alt={'Thumbnail of ' + item.snippet.title}
/>
<Media.Body>
<h5>{item.snippet.title}</h5>
<p> {item.snippet.channelTitle} - {ISO_8601parse(item.snippet.publishedAt)}</p>
<p>Reccomender: genreSimilarity</p>
</Media.Body>
</Link>
))
}</ul>
);
}}
}
export default genreSimilarity;
+68 -54
View File
@@ -1,8 +1,9 @@
import React from 'react'; import React from 'react';
import { Col, ListGroup, Card, CardDeck } from 'react-bootstrap'; // eslint-disable-line no-unused-vars import { Col, Media, Card } from 'react-bootstrap'; // eslint-disable-line no-unused-vars
import axios from 'axios'; // eslint-disable-line no-unused-vars import axios from 'axios'; // eslint-disable-line no-unused-vars
import { Link } from 'react-router-dom'; // eslint-disable-line no-unused-vars import { Link } from 'react-router-dom'; // eslint-disable-line no-unused-vars
import moment from "moment"; import moment from "moment";
var _ = require('lodash');
class popGlobaleAssoluta extends React.Component { class popGlobaleAssoluta extends React.Component {
state = { state = {
@@ -13,70 +14,54 @@ class popGlobaleAssoluta extends React.Component {
}; };
componentDidMount() { componentDidMount() {
// listSiti.map( // let listSiti = ['1828', '1829', '1838', '1839', '1846', '1822', '1847', '1831', '1827', '1848', '1824', '1830', '1836', '1850', '1849', '1851', '1861', '1823', '1863', '1834', '1904', '1906', '1901', '1862', '1859', '1841', '1905', '1864', '1840', '1860', '1858', '1835', '1911', '1913', '1826', '1855', '1907', '1912', '1903', '1915', '1854', '1910'];
// sito => let listSiti = ['1828', '1838','1839', '1846', '1831', '1827', '1823', '1863', '1834', '1901'];//, '1859', '1841', '1905', '1864', '1860', '1858', '1913', '1855', '1912', '1915', '1854', '1910'];
// axios.get(`http://site${sito}.tw.cs.unibo.it/globpop`, { Promise.all(
// 'params': { listSiti.map(
// 'id': this.props.match.params.id el => fetch(`http://site${el}.tw.cs.unibo.it/globpop?id=${this.props.match.params.id}`)
// } .then(res =>
// }) res.json()
// ) )
)
).then(res => {
// let listSiti = ['1828', '1829', '1838', '1839', '1846', '1822', '1847', '1831', '1827', '1848', '1824', '1830', '1836', '1850', '1849', '1851', '1861', '1823', '1863', '1834', '1904', '1906', '1901', '1862', '1859']; /* do something with res here... */
let listSiti = ['1828', '1838', '1839', '1846', '1847', '1831', '1827', '1849', '1823', '1863', '1834', '1901', '1859']; console.log(res)
let axiosReq = []; let tmp = res.map(x => x.recommended);
for(let i=0; i<listSiti.length; i++){ //let tmp2 =tmp.filter(el=>el.length > 0).map(el => el.map(el1 => el1.videoID || el1.videoId) )
console.log(`http://site${listSiti[i]}.tw.cs.unibo.it/globpop`) console.log(...tmp)
axiosReq.push(axios.get(`http://site${listSiti[i]}.tw.cs.unibo.it/globpop`, { this.getThumbnailsNames(tmp);
'params': { });
'id': this.props.match.params.id
}
})) }
}
Promise.all(axiosReq)
.then(axios.spread((...res) => {
console.log(res.map(x=>x.data))
})
)
}
// METODI // METODI
getThumbnailsNames(reccomended) { getThumbnailsNames(reccomended) {
let idToLookFor = reccomended.map(item => item.videoID); console.log(reccomended)
let tmp2 =reccomended.filter(el=>el.length > 0).map(el => el.map(el1 => el1.videoID || el1.videoId) )
let idToLookFor =[].concat(...tmp2);
idToLookFor.filter(el=>el.videoID !== undefined || el.videoId !== undefined)
axios.get('https://www.googleapis.com/youtube/v3/videos', { axios.get('https://www.googleapis.com/youtube/v3/videos', {
params: { params: {
part: 'snippet', part: 'snippet',
id: idToLookFor.toString(), id: _.uniq(idToLookFor).slice(0,50).toString(),
key: 'AIzaSyA21Odw9UdfiOJnxvA_eDfOW0OxuUfHKig', key: 'AIzaSyA21Odw9UdfiOJnxvA_eDfOW0OxuUfHKig',
fields: 'items(id,snippet/thumbnails/medium,snippet/title)' fields: 'items(id,snippet/thumbnails/high,snippet/title)'
} }
}) })
.then( .then(
res1 => { res1 => {
res1.data.items.map(resCurrentValue => {
Object.defineProperties(
reccomended.find(videoItem => {
return videoItem.videoID === resCurrentValue.id;
}),
{
'thumbnail':
{ value: resCurrentValue.snippet.thumbnails.medium.url },
'name':
{ value: resCurrentValue.snippet.title }
});
});
this.setState({ this.setState({
isLoaded: true, items: res1.data.items,
items: reccomended isLoaded: true,
}); });
console.log(res1)
}) })
} }
render() { render() {
const { error, isLoaded, items, headers } = this.state; const { error, isLoaded, items } = this.state;
if (error) { if (error) {
return ( return (
<React.Fragment> <React.Fragment>
@@ -91,7 +76,7 @@ class popGlobaleAssoluta extends React.Component {
</Col> </Col>
</React.Fragment> </React.Fragment>
); );
} else { } else { if (window.innerWidth < 992){
return ( return (
<React.Fragment> <React.Fragment>
{items.map(item => (<Col md="4"> {items.map(item => (<Col md="4">
@@ -99,16 +84,18 @@ class popGlobaleAssoluta extends React.Component {
<Link <Link
to={{ to={{
pathname: '/video/' + item.videoID, pathname: '/video/' + item.videoID,
search: "?ref=popGlobaleAssoluta"
}} }}
> >
<Card.Img <Card.Img
src={item.thumbnail} src={item.snippet.thumbnails.high.url}
alt={'Thumbnail of ' + item.name} alt={'Thumbnail of ' + item.snippet.title}
/> />
<Card.ImgOverlay> <Card.ImgOverlay>
<Card.Title className="bg-dark d-inline text-white"> <Card.Title className="bg-dark d-inline text-white">
{item.name}<br /> {item.snippet.title}<br />
{item.lastSelected} {item.snippet.channelTitle}<br />
<span>Reccomender: popGlobaleAssoluta</span>
</Card.Title> </Card.Title>
</Card.ImgOverlay> </Card.ImgOverlay>
</Link> </Link>
@@ -117,7 +104,34 @@ class popGlobaleAssoluta extends React.Component {
</React.Fragment> </React.Fragment>
); );
} }else {
return (
<ul className="list-unstyled">{
this.state.items.map(item => (
<Link
key={item.videoID}
as="li" className="media mx-5 my-1 suggItem"
to={{
pathname: `/video/${item.videoID}`,
search: '?ref=popGlobaleAssoluta'
}}>
<img
width={240}
height={120}
className="align-self-center mr-3 img-fluid"
src={item.snippet.thumbnails.high.url}
alt={'Thumbnail of ' + item.snippet.title}
/>
<Media.Body>
<h5>{item.snippet.title}</h5>
<p> {item.snippet.channelTitle}</p>
<p>Reccomender: popGlobaleAssoluta</p>
</Media.Body>
</Link>
))
}</ul>
);
}}
} }
} }
+145
View File
@@ -0,0 +1,145 @@
import React from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
import { Col, Media, Card } from 'react-bootstrap';
class popLocaleAss extends React.Component {
state = {
isLoaded: false,
error: null,
res: null
}
// static getDerivedStateFromProps(nextProps, prevState) {
// }
componentDidMount() {
axios
.get('http://site1854.tw.cs.unibo.it/videotrack')
.then(res => {
this.getThumbnailsNames(res.data.sort((a, b) => a.timesWatched - b.timesWatched).reverse());
}
, error => this.setState({ error }))
}
// shouldComponentUpdate(nextProps, nextState) {
// }
// componentDidUpdate(prevProps, prevState) {
// if (prevProps.match.params !== this.props.match.params) {
// this.setState({ isLoaded: false })
// }
// }
// componentWillUnmount() {
// }
getThumbnailsNames(reccomended) {
let idToLookFor = reccomended.map(item => item.id);
axios.get('https://www.googleapis.com/youtube/v3/videos', {
params: {
part: 'snippet',
id: idToLookFor.toString(),
key: 'AIzaSyA21Odw9UdfiOJnxvA_eDfOW0OxuUfHKig',
fields: 'items(id,snippet/thumbnails/medium,snippet/title,snippet/description)'
}
})
.then(
res1 => {
res1.data.items.map(resCurrentValue =>
Object.defineProperties(
reccomended.find(videoItem => {
return videoItem.id === resCurrentValue.id;
}),
{
'thumbnail':
{ value: resCurrentValue.snippet.thumbnails.medium.url },
'name':
{ value: resCurrentValue.snippet.title },
'description':
{ value: resCurrentValue.snippet.description }
})
);
this.setState({
isLoaded: true,
items: reccomended
});
})
}
render() {
if (this.state.error) {
return <React.Fragment>{this.state.error.message} -- {this.state.error.response.data.error.errors[0].reason} </React.Fragment>;
} else if (!this.state.isLoaded) {
return <Col className="text-center">
<div className="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
</Col>;
} else {
if (window.innerWidth > 992){
return (
<ul className="list-unstyled">{
this.state.items.map(item => (
<Link
key={item.id.videoId}
as="li" className="media mx-5 my-1 suggItem"
to={{
pathname: '/video/' + item.id,
search: "?ref=popLocaleAss"
}}>
<img
width={240}
height={120}
className="align-self-center mr-3"
src={item.thumbnail}
alt={'Thumbnail of ' + item.name}
/>
<Media.Body>
<h5>{item.name}</h5>
<p>{item.description.substring(0, 200)}</p>
<p>Visto: {item.timesWatched} volt{item.timesWatched === 1 ?"a":"e"} </p>
<p> Reccomender: popLocaleAss </p>
</Media.Body>
</Link>
))
}</ul>
);
}else {return (<React.Fragment>
{this.state.items.map(function (item, index) {
return (
<Col key={index} md='4'>
<Card>
<Link
to={{
pathname: '/video/' + item.id,
search: "?ref=popLocaleAss"
}}
>
<Card.Img
src={item.thumbnail}
alt={'Thumbnail of ' + item.name}
/>
<Card.ImgOverlay>
<Card.Title className="bg-dark d-inline text-white">
{item.name}<br />
<span>Visto: {item.timesWatched} volt{item.timesWatched === 1 ?"a":"e"} </span><br />
<span> Reccomender: popLocaleAss </span>
</Card.Title>
</Card.ImgOverlay>
</Link>
</Card>
</Col>
)
})}
</React.Fragment>);
}}
}
}
export default popLocaleAss;
+65 -31
View File
@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Col, ListGroup, Card, CardDeck } from 'react-bootstrap'; // eslint-disable-line no-unused-vars import { Col, Card, Media } from 'react-bootstrap'; // eslint-disable-line no-unused-vars
import axios from 'axios'; // eslint-disable-line no-unused-vars import axios from 'axios'; // eslint-disable-line no-unused-vars
import { Link } from 'react-router-dom'; // eslint-disable-line no-unused-vars import { Link } from 'react-router-dom'; // eslint-disable-line no-unused-vars
/*eslint no-console: ["error", { allow: ["warn", "error", "info"] }] */ /*eslint no-console: ["error", { allow: ["warn", "error", "info"] }] */
@@ -18,16 +18,20 @@ class PopRelLoc extends React.Component {
} }
componentDidMount() { componentDidMount() {
let tmp = JSON.parse(localStorage.getItem('lastWatched')); axios.get("http://site1854.tw.cs.unibo.it/videotrack",{
let j=tmp.findIndex(el=>el.id === localStorage["lastId"]); params: {
//this.getThumbnailsNames(tmp[j].prevVideos); "prevId" : localStorage['lastId']
if (tmp.length === 0 || localStorage["prevId"] === '1') }
{this.getThumbnailsNames(JSON.parse(localStorage.getItem('first')))} }).then(res=>{
else this.getThumbnailsNames(res.data)
{this.getThumbnailsNames(tmp[j].prevVideos)} this.setState({
items : res.data
})
},
error=>console.error(error))
} }
// METODI // METODI
getThumbnailsNames(listVideo) { getThumbnailsNames(listVideo) {
let idToLookFor = listVideo.map(item => item.id); let idToLookFor = listVideo.map(item => item.id);
@@ -80,33 +84,63 @@ class PopRelLoc extends React.Component {
</React.Fragment> </React.Fragment>
); );
} else { } else {
if (window.innerWidth < 992) {
return ( return (
<React.Fragment> <React.Fragment>
{items.map(item => ( {items.map(item => (
<Col md="4"> <Col md="4">
<Card key={item.id}> <Card key={item.id}>
<Link <Link
to={{ to={{
pathname: '/video/' + item.id, pathname: '/video/' + item.id,
}} search: "?ref=popRelLoc"
> }}
<Card.Img >
src={item.thumbnail} <Card.Img
alt={'Thumbnail of ' + item.name} src={item.thumbnail}
/> alt={'Thumbnail of ' + item.name}
<Card.ImgOverlay> />
<Card.Title className="bg-dark d-inline text-white"> <Card.ImgOverlay>
{item.name}<br /> <Card.Title className="bg-dark d-inline text-white">
{item.lastSelected} {item.name}<br />
</Card.Title> <span>Visto: {item.timesWatched} volt{item.timesWatched === 1 ?"a":"e"} </span><br />
</Card.ImgOverlay> <span> Reccomender: popRelLoc </span>
</Link> </Card.Title>
</Card.ImgOverlay>
</Link>
</Card> </Card>
</Col>))} </Col>))}
</React.Fragment> </React.Fragment>
); );
} }else{
return (
<ul className="list-unstyled">{
this.state.items.map(item => (
<Link
key={item.id.videoId}
as="li" className="media mx-5 my-1 suggItem"
to={{
pathname: '/video/' + item.id,
search: "?ref=popRelLoc"
}}>
<img
width={240}
height={120}
className="align-self-center mr-3"
src={item.thumbnail}
alt={'Thumbnail of ' + item.name}
/>
<Media.Body>
<h5>{item.name}</h5>
<p>Visto: {item.timesWatched} volt{item.timesWatched === 1 ?"a":"e"} </p>
<p> Reccomender: popRelLoc </p>
</Media.Body>
</Link>
))
}</ul>
);
}}
} }
} }
+148
View File
@@ -0,0 +1,148 @@
import React from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
import { Col, Media, Card } from 'react-bootstrap';
class similarityArtist extends React.Component {
state = {
isLoaded: false,
error: null,
res: null
}
// static getDerivedStateFromProps(nextProps, prevState) {
// }
componentDidMount() {
let timerId1;
let promise = new Promise((resolve, reject) => {
timerId1 = window.setInterval(() => {
if (localStorage['lastId'] === JSON.parse(localStorage['currentVideoInfo']).id)
resolve(true)
}, 1000)
})
promise.then(() => {
window.clearInterval(timerId1);
this.setState({ artist: JSON.parse(localStorage['currentVideoInfo']).artist })
axios.get('https://www.googleapis.com/youtube/v3/search', {
params: {
'part': 'snippet',
'q': JSON.parse(localStorage['currentVideoInfo']).artist,
'videoEmbeddable': 'true',
'type': 'video',
'maxResults': 15,
'topicId': '/m/04rlf, /m/02jjt',
'key': 'AIzaSyA21Odw9UdfiOJnxvA_eDfOW0OxuUfHKig'
}
}).then(
res => {
let j = res.data.items.findIndex(el => el.id.videoId === localStorage['lastId'])
if (j >= 0)
res.data.items.splice(j, 1);
this.setState({
res: res.data,
isLoaded: true
})
console.log(res);
}
,
error => this.setState({
error
})
)
})
}
// shouldComponentUpdate(nextProps, nextState) {
// }
// componentDidUpdate(prevProps, prevState) {
// if (prevProps.match.params !== this.props.match.params) {
// this.setState({ isLoaded: false })
// }
// }
// componentWillUnmount() {
// }
render() {
if (this.state.error) {
return <React.Fragment>{this.state.error.message} -- {this.state.error.response.data.error.errors[0].reason} </React.Fragment>;
} else if (!this.state.isLoaded) {
return <Col className="text-center">
<div className="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
</Col>;
} else {
if (window.innerWidth > 992) {
return (
<>
<h4>Artist Similarity: {this.state.artist}</h4>
<ul className="list-unstyled">{
this.state.res.items.map(item => (
<Link
key={item.id.videoId}
as="li" className="media mx-5 my-1 suggItem"
to={{
pathname: '/video/' + item.id.videoId,
search: "?ref=similarityArtist"
}}>
<img
width={240}
height={120}
className="align-self-center mr-3"
src={item.snippet.thumbnails.medium.url}
alt={'Thumbnail of ' + item.snippet.title}
/>
<Media.Body>
<h5>{item.snippet.title}</h5>
<p>{item.snippet.description}</p>
<p>Reccomender: similarityArtist</p>
</Media.Body>
</Link>
))
}</ul>
</>
);
}else{
return (<React.Fragment>
{this.state.res.items.map(function (item, index) {
return (
<Col key={index} md='4'>
<Card>
<Link
to={{
pathname: '/video/' + item.id.videoId,
search: "?ref=similarityArtist"
}}
>
<Card.Img
src={item.snippet.thumbnails.high.url}
alt={'Thumbnail of ' + item.snippet.title}
/>
<Card.ImgOverlay>
<Card.Title className="bg-dark d-inline text-white">
{item.snippet.title}<br />
{item.snippet.channelTitle}<br />
<span>Reccomender: similarityArtist</span>
</Card.Title>
</Card.ImgOverlay>
</Link>
</Card>
</Col>
)
})}
</React.Fragment>);
}}
}
}
export default similarityArtist;
+8 -8
View File
@@ -10,12 +10,12 @@
const isLocalhost = Boolean( const isLocalhost = Boolean(
window.location.hostname === 'localhost' || window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address. // [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' || window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4. // 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match( window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
) )
); );
export default function register() { export default function register() {
@@ -30,7 +30,7 @@ export default function register() {
} }
window.addEventListener('load', () => { window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL?process.env.PUBLIC_URL:'http://localhost:8000'}/service-worker.js`; const swUrl = `${process.env.PUBLIC_URL ? process.env.PUBLIC_URL : 'http://localhost:8000'}/service-worker.js`;
if (isLocalhost) { if (isLocalhost) {
// This is running on localhost. Lets check if a service worker still exists or not. // This is running on localhost. Lets check if a service worker still exists or not.
@@ -41,7 +41,7 @@ export default function register() {
navigator.serviceWorker.ready.then(() => { navigator.serviceWorker.ready.then(() => {
console.log( console.log(
'This web app is being served cache-first by a service ' + 'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://goo.gl/SC7cgQ' 'worker. To learn more, visit https://goo.gl/SC7cgQ'
); );
}); });
} else { } else {