Youtube music and video downloader

App.jsx 2.8KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  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. {
  16. type: 'MP3',
  17. handler: (video) => window.open('/mp3/'+ video.id),
  18. handlerAll: () => window.open(`/mp3/batch/${this.batchLinks()}`)
  19. },
  20. {
  21. type: 'MP4',
  22. handler: (video) => window.open('/mp4/'+ video.id),
  23. handlerAll: () => window.open(`/mp4/batch/${this.batchLinks()}`)
  24. },
  25. ];
  26. _.each(['inputChange', 'selectResult', 'removeLink'], f => this[f] = this[f].bind(this));
  27. }
  28. batchLinks() {
  29. return _.map(this.state.links, 'id').join(',');
  30. }
  31. inputChange(text) {
  32. clearTimeout(this.timeout);
  33. this.setState({ value: text }, () => this.timeout = setTimeout(this.search.bind(this), 100));
  34. }
  35. removeLink(link) {
  36. if (_.some(this.state.links, e => e.id === link.id))
  37. this.setState({ links: _.reject(this.state.links, e => e.id === link.id) });
  38. }
  39. selectResult(video) {
  40. if (_.every(this.state.links, e => e.id !== video.id)) {
  41. if (_.some(this.props.converted, e => e.id === video.id && e.audio && e.video))
  42. video.converted = _.find(this.props.converted, e => e.id === video.id)._id;
  43. else
  44. Meteor.call('links.add', {video, type: 'audio'}, (err, res) => {
  45. if (err)
  46. console.error(err);
  47. else
  48. video.converted = _.find(this.props.converted, e => e.id === video.id)._id;
  49. });
  50. this.setState({links: [...this.state.links, video]});
  51. }
  52. this.setState({ value: '', result: [] });
  53. }
  54. search() {
  55. if (!this.state.value)
  56. this.setState({ result: [] });
  57. else
  58. YouTubeSearch(this.state.value)
  59. .then(e => e.items.map(e => new Video(e)))
  60. .then(e => { this.setState({ result: e }); return e; })
  61. .then(e => YouTubeVideoInfo(_.map(e, 'id').join(','))
  62. .then(r => _.each(e, (elem, i) => elem.update(r.items[i].statistics, r.items[i].contentDetails.duration)))
  63. .then(() => this.setState({ result: e }))
  64. );
  65. }
  66. render() {
  67. return <AppLayout
  68. links={this.state.links} linkRemove={this.removeLink} linkHandlers={this.options}
  69. inputChange={this.inputChange} inputValue={this.state.value}
  70. resultSelect={this.selectResult} results={this.state.result}
  71. optionsHandler={this.options} converted={this.props.converted}
  72. />;
  73. }
  74. }
  75. App.propTypes = { converted: PropTypes.array.isRequired };
  76. export default createContainer(() => {
  77. Meteor.subscribe('links');
  78. return { converted: Converted.find({}).fetch(), };
  79. }, App);