diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 9554e0dd..00000000 --- a/.babelrc +++ /dev/null @@ -1,54 +0,0 @@ -{ - "presets": ["stage-0", "react"], - "env": { - "production": { - "presets": [ - [ - "env", - { - "targets": { "node": 10 }, - "modules": false, - "useBuiltIns": true - } - ], - "react-optimize" - ], - "plugins": ["dev-expression"] - }, - "test": { - "presets": [ - [ - "env", - { - "targets": { "node": 10 }, - "useBuiltIns": true - } - ] - ], - "plugins": [ - "transform-class-properties", - "transform-es2015-classes", - "dynamic-import-node" - ] - }, - "development": { - "presets": [ - [ - "env", - { - "targets": { "node": 10 }, - "useBuiltIns": true - } - ] - ], - "plugins": [ - "transform-class-properties", - "transform-es2015-classes" - // ["flow-runtime", { - // "assert": true, - // "annotate": true - // }] - ] - } - } -} diff --git a/.env.example b/.env.example index 250fa2cb..f60cdb8e 100644 --- a/.env.example +++ b/.env.example @@ -72,12 +72,9 @@ CONFIG_MAX_CONNECTIONS=20 # English subtitles support # -# This flag allows subtitles in torrents to be played along with the movie. Be -# careful, since this is a very early stage experiment! Currently this only -# allows English subtitles. Support for subtitles of multiple languages is -# planned for a future release. +# This flag allows subtitles in torrents to be played along with the movie -FLAG_SUBTITLES=false +FLAG_SUBTITLES=true diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 71c9e3b4..00000000 --- a/.eslintrc +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extends": ["airbnb", "prettier", "prettier/flowtype", "prettier/react"], - "parser": "babel-eslint", - "env": { - "browser": true, - "node": true - }, - "rules": { - "class-methods-use-this": "off", - "no-let": "off", - "no-plusplus": "off", - "no-console": "off", - "promise/avoid-new": "off", - "react/sort-comp": "off", - "react/jsx-filename-extension": "off", - "import/no-extraneous-dependencies": "off", - "no-nested-ternary": "off" - }, - "settings": { - "import/extensions": [".jsx", ".js"], - "webpack": { - "config": "webpack.config.eslint.js" - } - } -} diff --git a/.gitignore b/.gitignore index d1b4a9fc..e9654079 100644 --- a/.gitignore +++ b/.gitignore @@ -39,8 +39,6 @@ flow-typed/npm/* release app/main.prod.js app/main.prod.js.map -app/bundle.js -app/bundle.js.map app/style.css app/style.css.map dist diff --git a/.travis.yml b/.travis.yml index 2a4f463c..072eede9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ matrix: language: node_js node_js: - node - - 9 env: - ELECTRON_CACHE=$HOME/.cache/electron - ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder @@ -15,13 +14,12 @@ matrix: language: node_js node_js: - node - - 9 addons: apt: sources: - ubuntu-toolchain-r-test packages: - - g++-4.8 + - g++-8 - icnsutils - graphicsmagick - xz-utils @@ -49,7 +47,7 @@ cache: - "$HOME/docker" install: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export CXX="g++-4.8"; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export CXX="g++-8"; fi - yarn - | if [ "$TRAVIS_OS_NAME" == "linux" ]; then diff --git a/README.md b/README.md index 2a0f2975..36af82c0 100644 --- a/README.md +++ b/README.md @@ -24,19 +24,22 @@ +
+ +
+
-## Features: -**Modern**: This client was started from scratch and was designed to be performant and customizable +## Features: -**Performance**: Significantly faster than other clients. Everything from scrolling perf to playing movies is buttery smooth +* ✨ **Modern**: This client was started from scratch and was designed to be performant and customizable -**Faster Torrents**: New API optimized for fast torrents by querying the from multiple endpoints +* ⏩ **Performance**: Significantly faster than other clients. Everything from scrolling perf to playing movies is buttery smooth -**Modern Stack**: Electron, React, Redux, Webpack, ES8, Flow, and others +* 💨**Faster Torrents**: New API optimized for fast torrents by querying the from multiple endpoints -**Cross Platform**: Works on Mac, Windows, and Linux +* ✅**Cross Platform**: Works on Mac, Windows, and Linux ## Getting started: @@ -60,7 +63,7 @@ cd popcorn-time-desktop # 💡 For casting support, you will need to satisfy mdns's requirements: # For windows install bonjour: https://support.apple.com/downloads/bonjour_for_windows # For linux, make sure you have these dependencies installed with apt-get: -# https://github.com/amilajack/popcorn-time-desktop/blob/v1.2.0/.travis.yml#L24-L35 +# https://github.com/amilajack/popcorn-time-desktop/blob/v1.3.0/.travis.yml#L24-L35 # Install dependencies # Have a cup of coffee ☕️ this might take a while diff --git a/app/api/Player.js b/app/api/Player.js index bcb9dc4b..fe4c96dd 100644 --- a/app/api/Player.js +++ b/app/api/Player.js @@ -7,6 +7,8 @@ import vlcCommand from 'vlc-command'; import ChromecastPlayerProvider from './players/ChromecastPlayerProvider'; import type { metadataType } from './players/PlayerProviderInterface'; +export type subtitleType = { kind: string, src: string, srclang: string }; + const { powerSaveBlocker } = remote; export default class Player { @@ -62,11 +64,12 @@ export default class Player { async initCast( provider: ChromecastPlayerProvider, streamingUrl: string, - metadata: metadataType + metadata: metadataType, + subtitles: Array ) { this.powerSaveBlockerId = powerSaveBlocker.start('prevent-app-suspension'); const addr = streamingUrl.replace('localhost', network()); - return provider.play(addr, metadata); + return provider.play(addr, metadata, subtitles); } initYouTube() { diff --git a/app/api/Subtitle.js b/app/api/Subtitle.js index 5fd4de75..296fac3c 100644 --- a/app/api/Subtitle.js +++ b/app/api/Subtitle.js @@ -31,6 +31,14 @@ export default class SubtitleServer { // Start the static file server for the subtitle files const server = express(); + // Enable CORS + // https://github.com/thibauts/node-castv2-client/wiki/How-to-use-subtitles-with-the-DefaultMediaReceiver-app#subtitles + server.use((req, res, next) => { + if (req.headers.origin) { + res.headers['Access-Control-Allow-Origin'] = req.headers.origin; + } + next(); + }); server.use(express.static(this.basePath)); this.server = server.listen(this.port); diff --git a/app/api/Torrent.js b/app/api/Torrent.js index f2e0b6d2..e1bce575 100644 --- a/app/api/Torrent.js +++ b/app/api/Torrent.js @@ -65,7 +65,13 @@ export default class Torrent { magnetURI: string, metadata: metadataType, supportedFormats: Array, - cb + cb: ( + servingUrl: string, + file: { name: string }, + files: string, + torrent: string, + subtitle: { name: string } | boolean + ) => void ) { if (this.inProgress) { console.log('Torrent already in progress'); @@ -159,7 +165,8 @@ export default class Torrent { file, files, torrent, - selectSubtitleFile(files, activeMode, metadata) + false + // selectSubtitleFile(files, activeMode, metadata) ); this.clearIntervals(); diff --git a/app/api/players/ChromecastPlayerProvider.js b/app/api/players/ChromecastPlayerProvider.js index b2d9b6be..82ec6af4 100644 --- a/app/api/players/ChromecastPlayerProvider.js +++ b/app/api/players/ChromecastPlayerProvider.js @@ -1,10 +1,12 @@ // @flow import { Client, DefaultMediaReceiver } from 'castv2-client'; import mdns from 'mdns'; +import network from 'network-address'; import type { PlayerProviderInterface, deviceType, - metadataType + metadataType, + subtitleType } from './PlayerProviderInterface'; type castv2DeviceType = { @@ -81,13 +83,28 @@ class ChromecastPlayerProvider implements PlayerProviderInterface { return selectedDevice; } - play(contentUrl: string, metadata: metadataType) { + play( + contentUrl: string, + metadata: metadataType, + subtitles: Array + ) { const client = new Client(); if (!this.selectDevice) { throw new Error('No device selected'); } + const networkAddress = network(); + const tracks = subtitles.map((subtitle, index) => ({ + trackId: index, // This is an unique ID, used to reference the track + type: 'TEXT', // Default Media Receiver currently only supports TEXT + trackContentId: subtitle.src.replace('localhost', networkAddress), // the URL of the VTT (enabled CORS and the correct ContentType are required) + trackContentType: 'text/vtt', // Currently only VTT is supported + name: subtitle.srclang, // a Name for humans + language: subtitle.srclang, // the language + subtype: 'SUBTITLES' // should be SUBTITLES + })); + return new Promise((resolve, reject) => { client.connect( this.selectedDevice.address, @@ -101,6 +118,8 @@ class ChromecastPlayerProvider implements PlayerProviderInterface { contentType: 'video/mp4', streamType: 'BUFFERED', // or LIVE + tracks, + // Title and cover displayed while buffering metadata: { type: 0, @@ -117,10 +136,14 @@ class ChromecastPlayerProvider implements PlayerProviderInterface { } }; - player.load(media, { autoplay: true }, _err => { - if (_err) reject(_err); - resolve(); - }); + player.load( + media, + { autoplay: true, activeTrackIds: tracks.map(e => e.trackId) }, + _err => { + if (_err) reject(_err); + resolve(); + } + ); }); } ); diff --git a/app/api/torrents/BaseTorrentProvider.js b/app/api/torrents/BaseTorrentProvider.js index 9605e562..272b73a0 100644 --- a/app/api/torrents/BaseTorrentProvider.js +++ b/app/api/torrents/BaseTorrentProvider.js @@ -1,11 +1,11 @@ // @flow /* eslint prefer-template: 0 */ -import cache from 'lru-cache'; +import Cache from 'lru-cache'; import url from 'url'; import TheMovieDbMetadataProvider from '../metadata/TheMovieDbMetadataProvider'; import type { torrentType } from './TorrentProviderInterface'; -export const providerCache = cache({ +export const providerCache = new Cache({ maxAge: process.env.CONFIG_CACHE_TIMEOUT ? parseInt(process.env.CONFIG_CACHE_TIMEOUT, 10) * 1000 * 60 * 60 : 1000 * 60 * 60 // 1 hr @@ -250,15 +250,6 @@ export async function convertTmdbToImdb(tmdbId: string): Promise { return movie.ids.imdbId; } -// export async function convertImdbtoTmdb(imdbId: string): Promise { -// const theMovieDbProvider = new TheMovieDbMetadataProvider(); -// const movie = await theMovieDbProvider.getMovie(imdbId); -// if (!movie.ids.imdbId) { -// throw new Error('Cannot convert imdbId to tmdbId'); -// } -// return movie.ids.imdbId; -// } - export function formatSeasonEpisodeToString( season: number, episode: number diff --git a/app/app.global.scss b/app/app.global.scss new file mode 100644 index 00000000..cfe1d812 --- /dev/null +++ b/app/app.global.scss @@ -0,0 +1,35 @@ +// +// Modules +// + +@import "./styles/variables.scss"; + +// +// Bootstrap +// Core variables and mixins +// @TODO: Import only boootstrap that are used +// + +@import "~bootstrap/scss/bootstrap.scss"; +@import "~ionicons/dist/css/ionicons.min.css"; +@import "~plyr/src/sass/plyr.scss"; +@import "~notie/src/notie.scss"; + +// +// Components +// + +@import "./styles/components/Movie.scss"; +@import "./styles/components/CardList.scss"; +@import "./styles/components/Rating.scss"; +@import "./styles/components/Loader.scss"; +@import "./styles/components/Item.scss"; +@import "./styles/components/Show.scss"; +@import "./styles/components/SaveItem.scss"; +@import "./styles/components/Button.scss"; + +// +// Utilities +// + +@import "./styles/utilities/container.scss"; diff --git a/app/app.html b/app/app.html index 86b14372..3ec67f51 100644 --- a/app/app.html +++ b/app/app.html @@ -24,20 +24,20 @@ // Dynamically insert the DLL script in development env in the // renderer process if (process.env.NODE_ENV === 'development') { - scripts.push('../dll/vendor.dll.js'); + scripts.push('../dll/renderer.dev.dll.js'); } // Dynamically insert the bundled app script in the renderer process const port = process.env.PORT || 1212; scripts.push( - (process.env.HOT) - ? 'http://localhost:' + port + '/dist/bundle.js' - : './dist/bundle.js' + process.env.HOT + ? 'http://localhost:' + port + '/dist/renderer.dev.js' + : './dist/renderer.prod.js' ); document.write( scripts - .map(script => '