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 => '