Youtube music and video downloader

App.jsx 2.7KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import React, { Component, PropTypes } from 'react';
  2. import { Meteor } from 'meteor/meteor';
  3. import { createContainer } from 'meteor/react-meteor-data';
  4. import { Converted } from '../api/links';
  5. import { YouTubeSearch, YouTubeVideoInfo } from './YouTubeAPI';
  6. import Video from './Video';
  7. import AppLayout from './AppLayout';
  8. _ = lodash;
  9. class App extends Component {
  10. constructor(props) {
  11. super(props);
  12. this.state = { result: [], links: [], value: '' };
  13. this.timeout = undefined;
  14. this.options = [
  15. { type: 'MP3', handler: v => window.open('/mp3/'+ (v ? v.id : `batch/${this.batchLinks()}`)) },
  16. { type: 'MP4', handler: v => window.open('/mp4/'+ (v ? v.id : `batch/${this.batchLinks()}`)) },
  17. ];
  18. _.each(['inputChange', 'selectResult', 'removeLink'], f => this[f] = this[f].bind(this));
  19. }
  20. batchLinks() {
  21. return _.map(this.state.links, 'id').join(',');
  22. }
  23. inputChange(text) {
  24. clearTimeout(this.timeout);
  25. this.setState({ value: text }, () => this.timeout = setTimeout(this.search.bind(this), 100));
  26. }
  27. removeLink(link) {
  28. if (_.some(this.state.links, e => e.id === link.id))
  29. this.setState({ links: _.reject(this.state.links, e => e.id === link.id) });
  30. }
  31. selectResult(video) {
  32. if (_.every(this.state.links, e => e.id !== video.id)) {
  33. if (_.some(this.props.converted, e => e.id === video.id && e.audio && e.video))
  34. video.converted = _.find(this.props.converted, e => e.id === video.id)._id;
  35. else
  36. Meteor.call('links.add', {video, type: 'audio'}, (err, res) => {
  37. if (err)
  38. console.error(err);
  39. else
  40. video.converted = _.find(this.props.converted, e => e.id === video.id)._id;
  41. });
  42. this.setState({links: [...this.state.links, video]});
  43. }
  44. this.setState({ value: '', result: [] });
  45. }
  46. search() {
  47. if (!this.state.value)
  48. this.setState({ result: [] });
  49. else
  50. YouTubeSearch(this.state.value)
  51. .then(e => e.items.map(e => new Video(e)))
  52. .then(e => { this.setState({ result: e }); return e; })
  53. .then(e => YouTubeVideoInfo(_.map(e, 'id').join(','))
  54. .then(r => _.each(e, (elem, i) => elem.update(r.items[i].statistics, r.items[i].contentDetails.duration)))
  55. .then(() => this.setState({ result: e }))
  56. );
  57. }
  58. render() {
  59. return <AppLayout
  60. links={this.state.links} linkRemove={this.removeLink} linkHandlers={this.options}
  61. inputChange={this.inputChange} inputValue={this.state.value}
  62. resultSelect={this.selectResult} results={this.state.result}
  63. optionsHandler={this.options} converted={this.props.converted}
  64. />;
  65. }
  66. }
  67. App.propTypes = { converted: PropTypes.array.isRequired };
  68. export default createContainer(() => {
  69. Meteor.subscribe('links');
  70. return { converted: Converted.find({}).fetch(), };
  71. }, App);