diff --git a/.appveyor.yml b/.appveyor.yml
deleted file mode 100644
index 3d0dcea8ca..0000000000
--- a/.appveyor.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-skip_commits:
- files:
- - .travis.yml
-
-environment:
- matrix:
- - nodejs_version: STABLE
-
-cache:
- - node_modules -> package.json
-
-build_script:
- - yarn config set yarn-offline-mirror ./node_modules/
- - yarn install --ignore-engines --ignore-scripts
- - yarn build
-
-after_build:
- - yarn gulp compress
-
-artifacts:
- - path: build\*.zip
- name: PopcornTime
-
-deploy:
- description: 'Windows Release'
- provider: GitHub
- auth_token:
- secure: wKYGmYxyZoGANT1qraz3HzsUN9tiYx9pvF3KpILJZ/UQtC+EC5XHsnxKNtCjDUhb # your encrypted token from GitHub
- artifact: /build/.*\.zip/
- on:
- branch: master
- appveyor_repo_tag: false
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index f8022b6469..7c72f50175 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -11,35 +11,34 @@ assignees: ''
If you are asking a question rather than filing a bug, try one of these instead:
- Wiki & FAQ (https://github.com/popcorn-official/popcorn-desktop/wiki)
- Reddit /r/PopCornTimeApp (https://www.reddit.com/r/PopCornTimeApp/)
-- Popcorn Time Forum (https://discuss.popcorntime.app/)
-->
-Operating System Version:
-
+#### Operating System Version:
+
-Popcorn Time Version:
+#### Popcorn Time Version:
-Download date:
+#### Download date:
-Download url:
+#### Download url:
-#### Expected Behaviour
+#### Expected Behaviour:
...
-#### Actual Behaviour
+#### Actual Behaviour:
...
-#### Steps to reproduce the behaviour
+#### Steps to reproduce the behaviour:
1. ...
2. ...
3. ...
-#### Screenshot(s) of issue or error(s) logs of developer console (Windows/Linux: F12, MacOS: ⌘ + 0 ... then 'console' tab) (recommended)
+#### Screenshot(s) of issue or error(s) logs of developer console (Windows/Linux: F12, MacOS: ⌘ + 0 ... then 'console' tab) (recommended):
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index cfb2e76442..a30861d03c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -15,7 +15,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
- nwjs: ['0.44.5', '0.82.0']
+ nwjs: ['0.44.5', '0.86.0']
steps:
- name: Context
@@ -51,26 +51,75 @@ jobs:
key: "${{ matrix.os }}"
map: |
{
- "ubuntu-latest": { "platform": "linux", "dist": "linux32,linux64" },
- "macOS-latest": { "platform": "osx", "dist": "osx64" },
- "windows-latest": { "platform": "win", "dist": "win32,win64" }
+ "ubuntu-latest": { "platform": "linux64" },
+ "macOS-latest": { "platform": "osx64" },
+ "windows-latest": { "platform": "win64" }
}
- name: Build info
- run: echo Build ${{ env.dist }} on nw-v${{ matrix.nwjs }}
+ run: echo Build ${{ env.platform }} on nw-v${{ matrix.nwjs }}
- name: Build App
run: |
yarn
- yarn gulp dist --platforms=${{ env.platform == 'win' && '"' || '' }}${{ env.dist }}${{ env.platform == 'win' && '"' || '' }} --nwVersion=${{ matrix.nwjs }}
+ yarn gulp dist --platforms=${{ env.platform == 'win' && '"' || '' }}${{ env.platform }}${{ env.platform == 'win' && '"' || '' }} --nwVersion=${{ matrix.nwjs }}
- name: Upload artifacts
uses: actions/upload-artifact@master
with:
name: ${{ env.platform }}-${{ matrix.nwjs }}
path: build
- release:
+ packs:
needs: build
+ name: Packs
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ nwjs: ['0.44.5', '0.86.0']
+
+ steps:
+ - name: Context
+ env:
+ GITHUB_CONTEXT: ${{ toJson(github) }}
+ run: echo "$GITHUB_CONTEXT"
+
+ - uses: actions/checkout@v4
+ with:
+ path: repo
+ persist-credentials: false
+
+ - uses: actions/download-artifact@v4
+ with:
+ name: linux64-${{ matrix.nwjs }}
+ path: .
+
+ - name: Install packages and appimagetool
+ run: |
+ sudo apt update
+ sudo apt install -y libfuse2
+ wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
+ chmod +x appimagetool-x86_64.AppImage
+
+ - name: Build AppImage
+ run: |
+ VER=$(ls *-linux64*.zip | sed 's/-linux64.*//')
+ echo $VER
+ unzip -q *-linux64*.zip -d $VER.AppDir
+ cp repo/dist/linux/appimage/* $VER.AppDir/
+ ln -s Popcorn-Time $VER.AppDir/AppRun
+ mkdir build
+ ./appimagetool-x86_64.AppImage $VER.AppDir build/$VER-linux64${{ matrix.nwjs == '0.44.5' && '-0.44.5' || '' }}.AppImage
+
+ - name: Upload artifacts
+ uses: actions/upload-artifact@master
+ with:
+ name: linux64-${{ matrix.nwjs }}.AppImage
+ path: build
+
+
+ release:
+ needs: packs
name: Release
runs-on: ubuntu-latest
steps:
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 730bfc8d1a..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-os:
- - linux
- - osx
-
-language: node_js
-node_js:
- - stable
-cache:
- yarn: true
- directories:
- - node_modules
-before_install:
- - yarn config set yarn-offline-mirror ./node_modules/
- - yarn install --ignore-engines
- - yarn build
-after_success:
- - ls -ltr ./build/
-deploy:
- provider: releases
- file_glob: true
- file: build/Popcorn-Time*.zip
- name: Build $(date +'%d.%m.%Y %R')
- skip_cleanup: true
- on:
- repo: popcorn-official/popcorn-desktop
- branch: development
- tags: false
- api-key:
- secure: "IxNjbYB5ptBG6yvdTVV5otXxOYUO6L31VRxHAL/3A939MukMM6GUBlr0jcJ2mWzxvhHoo90r3RWT/81qCPH0Mf7oDCWthwuAsX1RtOVC7LinbEeLEX4Lp/To0kC2gn0dBwXSJhakkQZ4alZywRxLzV+TxiF/HYlOLuF+6ZZ5mtv6vNIylxkmaQy/KHV43LYLw7weEHafj3TSpLsKFZw0/Rqw/rKKWCMzMY750HmN0rNb54Hbu9+5zVdNpnfa4ovmdTiB7aOjrr2/Z8LVYdw9+MTipwpYq4Kb2syYjkZiduUshI+ttsyw6T5Hgg0hrAnW3pVZ+HLnxQUw75BnUOoF3rFFEdLA3tGQ1BGf2zRoN10JQae/hBAvagY5Y+55m02IUE1qnOCJ6vrAfMb2a1i4Eu+JUCViMw/ITKEYnGkHWyvZ1uhT3M8WqEUAZWCnNlRz2+UjZD4dXOwQwvYzBG5smDW2H1/Z0XRUJjCGU+G5Gvol4qa7Dsb97d6ezQvtqs//DI80Af0IgQawiBVwMboLlgqUCra6+1WqlwzVk22OznkBR3zJDTqJwNkBrXhzmpy6/y2MiqcsctgMsJHClHHy4CD3PK4LplO1IHs0Ix/QZEN6gBUN3vsJsgZjhUuiPD8p0rsnxeCC8XF7GnhJOnnh6RpzF7ZOs1FjcQ+iEixgMGY="
-notifications:
- email: false
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 031a0af76b..8fc1103433 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,35 @@
+## 0.5.1 - Now.. Bring me that Horizon - 16 April 2024
+
+New Features:
+- Update NW.js runtime to 0.86.0 (0.44.5 still supported as an option for this release)
+- Add Always show bookmark over covers option
+- Improve local file support adding external players/cast devices
+- Add option to rescan external players/cast devices
+- Include PATH env. variable when scanning for external players
+- Improve Torrent Collection/Seedbox interaction
+- Add Linux AppImage
+
+Bug Fixes:
+- Fix native player UI issue when in fullscreen
+- Fix Favorites/Watched tabs Anime category
+- Fix issue with missing covers when TMDb is inaccessible
+- Fix issue with displaying incorrect filters in some instances
+- Fix issue with the settings/databases flushing functions
+- Tooltip fixes
+
+Other:
+- Replace the old Help screen with the online FAQ
+- Update the build system
+- Clean up obsolete/unnecessary code
+- Update torrent trackers
+- Update various modules/dependencies
+- Various other small fixes and optimizations
+
## 0.5.0 - Mischief Managed - 10 February 2024
New Features:
- Update NW.js runtime to 0.82.0 (0.44.5 still supported as an option for this release)
+- Add macOS build for Apple Silicon (NW.js 0.82.0 only)
- Add working Anime tab
- Add Watched tab
- Add Seedbox option for exiting the app when downloads complete
diff --git a/README.md b/README.md
index cf719e03e4..6be80aebb8 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
+
Popcorn Time
@@ -14,15 +14,14 @@
-
-
-
+
+
+
-
-
+
***
@@ -30,13 +29,13 @@
### Windows:
Download and install:
- * **Latest release**: check [popcorntimeapp.netlify.app](https://popcorntimeapp.netlify.app) or the repo's [releases page](https://github.com/popcorn-official/popcorn-desktop/releases)
+ * **Latest release**: check [popcorn-time.site](https://popcorn-time.site) or the repo's [releases page](https://github.com/popcorn-official/popcorn-desktop/releases)
* Or **latest dev build (for testers)**: check the repo's [actions page](https://github.com/popcorn-official/popcorn-desktop/actions)
### macOS:
Download and install:
- * **Latest release**: check [popcorntimeapp.netlify.app](https://popcorntimeapp.netlify.app) or the repo's [releases page](https://github.com/popcorn-official/popcorn-desktop/releases)
+ * **Latest release**: check [popcorn-time.site](https://popcorn-time.site) or the repo's [releases page](https://github.com/popcorn-official/popcorn-desktop/releases)
* Or **latest dev build (for testers)**: check the repo's [actions page](https://github.com/popcorn-official/popcorn-desktop/actions)
Easily install Popcorn Time via _[Homebrew](https://brew.sh) ([Cask](https://docs.brew.sh/Cask-Cookbook)):_
@@ -57,7 +56,7 @@ Also, if you keep a [_Brewfile_](https://github.com/Homebrew/homebrew-bundle#usa
### Linux - Debian/Ubuntu based distros:
Download and install:
- * **Latest release**: check [popcorntimeapp.netlify.app](https://popcorntimeapp.netlify.app) or the repo's [releases page](https://github.com/popcorn-official/popcorn-desktop/releases)
+ * **Latest release**: check [popcorn-time.site](https://popcorn-time.site) or the repo's [releases page](https://github.com/popcorn-official/popcorn-desktop/releases)
* Or **latest dev build (for testers)**: check the repo's [actions page](https://github.com/popcorn-official/popcorn-desktop/actions)
Via .deb package:
@@ -67,13 +66,13 @@ Via .deb package:
Via archive and command line (tested on ubuntu 18.04 and 20.04):
1. Download Popcorn Time archive from the github repo for the **latest release** :
- `wget -c https://github.com/popcorn-official/popcorn-desktop/releases/download/v0.5.0/Popcorn-Time-0.5.0-linux64.zip`
+ `wget -c https://github.com/popcorn-official/popcorn-desktop/releases/download/v0.5.1/Popcorn-Time-0.5.1-linux64.zip`
2. Create popcorn-time folder in /opt/:
`sudo mkdir /opt/popcorn-time`
3. Install unzip && dependencies (they should not be always required but some users needed them to make Popcorn Time working):
`sudo apt update && sudo apt install unzip libcanberra-gtk-module libgconf-2-4 libatomic1`
4. Extract the zip in /opt/popcorn-time:
- `sudo unzip Popcorn-Time-0.5.0-linux64.zip -d /opt/popcorn-time`
+ `sudo unzip Popcorn-Time-0.5.1-linux64.zip -d /opt/popcorn-time`
5. Create symlink of Popcorn-Time in /usr/bin:
`sudo ln -sf /opt/popcorn-time/Popcorn-Time /usr/bin/popcorn-time`
6. Create .desktop file (so the launcher):
@@ -111,7 +110,7 @@ If you encounter trouble with the above method, you can try:
Optionally, you may simply run `./make_popcorn.sh` if you are on a linux or mac based operating system.
-Full instructions & troubleshooting tips can be found in the [Contributing Guide](CONTRIBUTING.md#contributing-to-popcorn-time).
+Full instructions & troubleshooting tips can be found in the [Contributing Guide](docs/Contributing.md#contributing-to-popcorn-time).
#### Building redistribuable packages/installers:
@@ -127,7 +126,7 @@ Redistribuable packages are saved into `build/` subfolder.
## Getting Involved
Want to report a bug, request a feature, contribute to or translate Popcorn Time?
-Check out our in-depth guide to [Contributing to Popcorn Time](CONTRIBUTING.md#contributing-to-popcorn-time). We need all the help we can get!
+Check out our in-depth guide to [Contributing to Popcorn Time](docs/Contributing.md#contributing-to-popcorn-time). We need all the help we can get!
You can also join our [community](README.md#community) to keep up-to-date and meet other developers.
@@ -135,7 +134,7 @@ You can also join our [community](README.md#community) to keep up-to-date and me
## Community
Keep track of Popcorn Time development and community activity.
* Read and contribute to the official [Popcorn Time Wiki](https://github.com/popcorn-official/popcorn-desktop/wiki/).
- * Join in discussions on the [Popcorn Time Forum](https://discuss.popcorntime.app) and [r/PopCornTimeApp](https://www.reddit.com/r/PopcornTimeApp).
+ * Join in discussions on [r/PopCornTimeApp](https://www.reddit.com/r/PopcornTimeApp).
## Screenshots
diff --git a/dist/linux/appimage/Popcorn-Time.desktop b/dist/linux/appimage/Popcorn-Time.desktop
new file mode 100644
index 0000000000..9484d177a8
--- /dev/null
+++ b/dist/linux/appimage/Popcorn-Time.desktop
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Comment=Watch Movies and TV Shows instantly
+Name=Popcorn-Time
+Exec=Popcorn-Time %U
+Icon=Popcorn-Time
+MimeType=application/x-bittorrent;x-scheme-handler/magnet;
+StartupNotify=false
+Categories=AudioVideo
+Type=Application
+X-Desktop-File-Install-Version=0.26
+
diff --git a/dist/linux/appimage/Popcorn-Time.png b/dist/linux/appimage/Popcorn-Time.png
new file mode 100644
index 0000000000..921f38212c
Binary files /dev/null and b/dist/linux/appimage/Popcorn-Time.png differ
diff --git a/dist/linux/bash-deb-builder.sh b/dist/linux/bash-deb-builder.sh
deleted file mode 100644
index cca116a9dc..0000000000
--- a/dist/linux/bash-deb-builder.sh
+++ /dev/null
@@ -1,141 +0,0 @@
-#!/bin/bash
-# published under GPL3.0
-
-# don't modify this file, see 'package-linux' instead
-# requires : build-essential
-
-. $1 #get information about the package
-
-version=$(cat ../../package.json | sed '/version/!d' | sed s/\"version\"://g | sed s/\"//g | sed s/\ //g | sed s/,//g) #specific to node-webkit apps
-
-scriptdir=$(echo $PWD) #bash-deb-builder.sh dir
-maindir=$(cd ../.. && echo $PWD) #main dir
-
-func_series() {
- mainseries=$(echo $series | sed "s/ .*//g")
- remainingseries=$(echo $series | sed "s/$mainseries //g")
-}
-
-func_name() {
- name="${name,,}" #no uppercase
- name="$(echo $name | tr ' ' '-')" #no spaces
-}
-
-func_maintainer() {
- export DEBEMAIL="$email"
- export DEBFULLNAME="$maintainer"
-}
-
-func_dep() {
- [[ -z "$dependencies" ]] || [[ "$dependencies" == "none" ]] || [[ "$dependencies" == "null" ]] && dependencies=
-}
-
-func_exclude() {
- exclude=$(echo "$exclude" | sed 's/ / **\//g') #globstar hack
-}
-
-func_size() {
- size=$(du -s PACKAGE | cut -f1)
-}
-
-func_appfiles() {
- echo -e "\n- Copying app files to new directory" #DEBUG
-
- shopt -s globstar
- func_name #transforms unwanted caracters in name entry
-
- for arch in "32" "64" ; do
- [ $arch == "32" ] && files=$(echo $files32 | sed "s/32/$arch/g")
- [ $arch == "64" ] && files=$(echo $files64 | sed "s/64/$arch/g")
-
- mkdir -p PACKAGE/$name-$version/$arch/$installdir/$name
- if [[ ! -z $launcher ]] ; then
- mkdir -p PACKAGE/$name-$version/$arch/usr/share/applications
- echo "$launcher" &> PACKAGE/$name-$version/$arch/usr/share/applications/$name.desktop
- chmod 644 PACKAGE/$name-$version/$arch/usr/share/applications/$name.desktop
- fi
- cp -r $files PACKAGE/$name-$version/$arch/$installdir/$name
- cd PACKAGE/$name-$version/$arch/$installdir/$name
- func_exclude
- rm -rf **/$exclude **/*.*~ &> /dev/null
- cd $scriptdir
- done
-}
-
-func_modify() {
- #changelog
- func_series
- sed -i "s/unstable/$mainseries/g" changelog
- sed -i "s/$version-1/$version/g" changelog
- sed -i "s/(Closes: #nnnn) //g" changelog
-
- #control
- sed -i "s/unknown/$section/g" control
- sed -i '/Homepage/d' control
- sed -i "s//$short_description/g" control
- sed -i '/long description/d' control
- echo "$long_description" | tee -a control &> /dev/null
- echo "Homepage: $homepage" | tee -a control &> /dev/null
- func_dep #makes sure that dependencies are well written if empty
- sed -i "s/\${shlibs:Depends}/$dependencies/g" control
- sed -i '7,8d' control
-
- #copyrights
- echo "$copyright" &> copyright
-
- #rules
- echo "$rules" &> rules
-
- #post-install and remove script
- echo "$postinst" &> postinst
- echo "$prerm" &> prerm
- sudo chmod 755 post* pre*
-}
-
-func_basefiles() {
- echo -e "\n- Creating tar.xz archive" #DEBUG
-
- cd PACKAGE/$name-$version
- tar --xz -cf ../$name"_"$version".orig.tar.xz" *
-
- echo -e "\n- Creating 'debian' directory" #DEBUG
-
- dh_make -s -y -c $license -f $name"_"$version".orig.tar.xz"
- cd debian && rm -rf *ex *EX README* docs
- func_modify #insert modification to changelog, copyright, rules, control, etc.
- #TODO:changelog is not detailed.
- cd $scriptdir
-}
-
-func_build() {
- echo -e "\n- Build with 'dpkg-buildbackage'" #DEBUG
-
- cd PACKAGE/$name-$version
- dpkg-buildpackage -S -rfakeroot -pgpg -k$gpgkey
- cd $scriptdir
-}
-
-func_postbuild() {
- mv PACKAGE sources"_"$version
- rm -rf sources"_"$version/$name-$version
-}
-
-func_upload() {
- echo -e "\n- Uploading to $ppa" #DEBUG
-
- cd sources"_"$version
- dput $ppa $name"_"$version"_source.changes"
- cd $scriptdir
-
- echo -e "\n- The package is only listed in '$mainseries'. You might want to go on Launchpad and publish the packages under other series too." #DEBUG
-}
-
-#Exec
-rm -rf PACKAGE sources"_"$version #always clean directories
-func_maintainer #set environment variables
-func_appfiles #adds the program files
-
-func_basefiles #creates needed files
-func_build #create .dsc
-func_postbuild #clean working dir and keep sources
-[ ! -z $ppa ] && func_upload #uploads to the given PPA.
diff --git a/dist/linux/copy-libatomic.sh b/dist/linux/copy-libatomic.sh
new file mode 100755
index 0000000000..19f6cebfd3
--- /dev/null
+++ b/dist/linux/copy-libatomic.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+# launch 'copy-libatomic.sh '
+
+builddir=$1
+projectName=$2
+arch=$3
+
+outDir="$1/$2/$3"
+
+sudo dpkg --add-architecture i386
+dpkg-query -s libatomic1
+if [ ! $? = 0 ]; then
+ sudo apt update
+ sudo apt install -y libatomic1
+fi
+dpkg-query -s libatomic1:i386
+if [ ! $? = 0 ]; then
+ sudo apt update
+ sudo apt install -y libatomic1:i386
+fi
+
+if [[ $arch == "linux64" ]]
+then
+ read source <<< `readlink -f /usr/lib/x86_64*/libatomic.so.*`
+else
+ read source <<< `readlink -f /usr/lib/i386*/libatomic.so.*`
+fi
+cp $source "$outDir/lib/libatomic.so.1"
diff --git a/dist/linux/exec_basefile.sh b/dist/linux/exec_basefile.sh
deleted file mode 100644
index 5d709eb37f..0000000000
--- a/dist/linux/exec_basefile.sh
+++ /dev/null
@@ -1,113 +0,0 @@
-#!/bin/bash
-
-#Error
-func_error() {
-[ $error == "0" ] && return 0
-echo "
-Unexpected Error:
-=================
-at: $current
-... Please try again."
-exit 1
-}
-
-#Get current architecture
-current="1:Set architecture"
-if [[ $(uname --machine) == "x86_64" ]] ; then
- arch="64" && error=0
-elif [[ $(uname --machine) == "i"*"86" ]] ; then
- arch="32" && error=0
-else
- error=1
-fi
-func_error
-
-#Variables
-version="BT_VERSION"
-tos="http://butterproject.org/tos.html"
-
-#Disclaimer
-clear
-echo "
-Butter $version - Linux $arch bits
-==================================
-
-Please read our Terms of service:
- $tos
-
-This installer will install Butter in:
- ~/.Butter
- ~/.local/share/applications
- ~/.local/share/icons
-"
-
-{ read -p "To continue, type 'I agree': " r /dev/null && error=0 || error=1
-
-#move icon
-mkdir -p "$HOME/.local/share/icons"
-cp butter.png "$HOME/.local/share/icons/butter.png" &> /dev/null && error=0 || error=1
-
-func_error
-
-#create .desktop in home
-echo "
-- Creating new configuration files..."
-
-current="2: Desktop file"
-mkdir -p "$HOME/.local/share/applications"
-
-echo "[Desktop Entry]
-Comment=Watch Movies and TV Shows instantly
-Name=Butter
-Exec=$HOME/.Butter/Butter
-Icon=butter.png
-MimeType=application/x-bittorrent;x-scheme-handler/magnet;
-StartupNotify=false
-Categories=AudioVideo;Video;Network;Player;P2P;
-Type=Application" > "$HOME/.local/share/applications/Butter.desktop" && error=0 || error=1
-func_error
-
-# Work-around for missing libudev.so.1 on Ubuntu 12.04
-if [ ! -e /lib/$(uname --machine)-linux-gnu/libudev.so.1 ]; then
- ln -s /lib/$(uname --machine)-linux-gnu/libudev.so.0 $HOME/.Butter/libudev.so.1
- sed -i 's,Exec=,Exec=env LD_LIBRARY_PATH='"$HOME"'/.Butter ,g' $HOME/.local/share/applications/Butter.desktop
-fi
-
-#chmod .desktop
-current="3: Chmod files"
-chmod +x "$HOME/.Butter/Butter/Butter" &> /dev/null && error=0 || error=1
-chmod +x "$HOME/.local/share/applications/Butter.desktop" &> /dev/null && error=0 || error=1
-func_error
-
-#uninstaller
-echo "How to uninstall Butter ?
-===============================
-
-1) Main application:
-- Delete ~/.Butter
-- Delete ~/.local/share/applications/Butter.desktop
-- Delete ~/.local/share/icons/butter.png
-
-2) Configuration files and databases:
-- Delete ~/.config/Butter" > "$HOME/.Butter/Uninstall.txt"
-
-#installation success
-echo "
-
-Butter is now installed in:
- «$HOME/.Butter»
-"
diff --git a/dist/linux/exec_dist_installer.sh b/dist/linux/exec_dist_installer.sh
deleted file mode 100644
index bd96075162..0000000000
--- a/dist/linux/exec_dist_installer.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-
-#remove old script
-rm -rf dist/linux/linux-dist-installer
-
-#copy basefile
-cp dist/linux/exec_dist_basefile.sh dist/linux/linux-dist-installer
-
-#get version from package.json
-version=$(cat package.json | sed '/version/!d' | sed s/\"version\"://g | sed s/\"//g | sed s/\ //g | sed s/\ //g | sed s/,//g)
-
-#write version in script
-sed -i "s/BT_VERSION/$version/g" dist/linux/linux-dist-installer
diff --git a/dist/linux/exec_installer.sh b/dist/linux/exec_installer.sh
deleted file mode 100644
index 09c8f23fb1..0000000000
--- a/dist/linux/exec_installer.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-
-#remove old script
-rm -rf dist/linux/linux-installer
-
-#copy basefile
-cp dist/linux/exec_basefile.sh dist/linux/linux-installer
-
-#get version from package.json
-version=$(cat package.json | sed '/version/!d' | sed s/\"version\"://g | sed s/\"//g | sed s/\ //g | sed s/\ //g | sed s/,//g)
-
-#write version in script
-sed -i "s/BT_VERSION/$version/g" dist/linux/linux-installer
diff --git a/dist/linux/package-information b/dist/linux/package-information
deleted file mode 100644
index b0d6b10412..0000000000
--- a/dist/linux/package-information
+++ /dev/null
@@ -1,196 +0,0 @@
-#!/bin/bash
-# Package information for bash-deb-builder
-
-#Project name. Will be transformed with lowercases and replaced with a dash.
-name="My Application"
-
-#Version of *buntu to target.
-series="trusty"
-
-#The required dependencies for your package. Can be empty.
-dependencies="thisdep (>= 110) | thatdep (>= 218)"
-
-#Full name & email of the package maintainer.
-maintainer="My Name"
-email="myadress@mymail.com"
-
-#Your public GPG key or the one from your launchpad team. Also known as OpenPGP key.
-gpgkey="8 char public key"
-
-#To automatically upload the archive to a remote PPA. Uncomment to use.
-ppa="ppa:owner/name"
-
-#Project URL if needed, can be empty.
-homepage="http://my-website.com"
-
-#60 char. max.
-short_description="My Application is really cool"
-
-#60 char. max. per line. Always indent with a
-long_description=" My Application allows you to use it
- for doing things and stuff.
- It's a complete application with feature 1
- and feature 2 just for you."
-
-#The section must exist. If you don't know, choose "misc"
-section="web"
-
-#Where will the package be installed on the user's machine? Currently doesn't work with subdirectories
-installdir="/opt"
-
-#Files to be included in the package
-files64="thisfile thatfile"
-
-files32="thisfile thatfile andthisfiletoo"
-
-#Files or formats to exclude from the package. Wildcard * allowed.
-exclude="this that* *andallofthis*"
-
-#License tag. Must be: apache|artistic|bsd|gpl|gpl2|gpl3|lgpl|lgpl2|lgpl3|mit
-license="gpl3"
-
-#Launcher for Linux. Will be in a .desktop file.
-launcher="[Desktop Entry]
-Comment=My App is cool
-Name=My Application
-Exec=$installdir/my-app/binary
-Icon=$installdir/my-app/icon.png
-MimeType=application/x-bittorrent;x-scheme-handler/magnet;
-StartupNotify=false
-Type=Application"
-
-#Specific rules for Butter
-rules="#!/usr/bin/make -f
-
-DEB_BUILD_GNU_TYPE ?= \$(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
-DEB_HOST_ARCH ?= \$(shell dpkg-architecture -qDEB_HOST_ARCH)
-DEB_HOST_ARCH_CPU ?= \$(shell dpkg-architecture -qDEB_HOST_ARCH_CPU)
-DEB_HOST_ARCH_OS ?= \$(shell dpkg-architecture -qDEB_HOST_ARCH_OS)
-DEB_HOST_GNU_TYPE ?= \$(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
-
-configure: configure-stamp
-configure-stamp:
- dh_testdir
- touch configure-stamp
-
-build: build-arch build-indep
-
-build-arch: build-stamp
-build-indep: build-stamp
-
-build-stamp: configure-stamp
- touch \$@
-
-clean:
- dh_testdir
- dh_testroot
- rm -f build-stamp
- dh_clean
-
-install: build
- dh_testdir
- dh_testroot
- dh_prep
- dh_installdirs
-
-ifeq (\$(DEB_HOST_ARCH),amd64)
- cp -arf 64/usr \$(CURDIR)/debian/butter/
- cp -arf 64/opt \$(CURDIR)/debian/butter/
-else
- cp -arf 32/usr \$(CURDIR)/debian/butter/
- cp -arf 32/opt \$(CURDIR)/debian/butter/
-endif
-
-# Build architecture-independent files here.
-binary-indep: build install
-# We have nothing to do by default.
-
-# Build architecture-dependent files here.
-binary-arch: build install
- dh_testdir
- dh_testroot
- dh_installchangelogs
- dh_installdocs
- dh_icons
- dh_installmenu
- dh_link
- dh_strip
- dh_compress
- dh_fixperms
- dh_installdeb
- dh_gencontrol
- dh_md5sums
- dh_builddeb
-
-binary: binary-indep binary-arch
-.PHONY: build clean binary-indep binary-arch binary install"
-
-
-#A text file formatted following http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
-copyright="Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
-Upstream-Name: Butter
-Source: https://github.com/butterproject/butter-desktop
-
-Files: *
-Copyright: 2014 Butter Project and the contributors
-License: GPL-3.0+
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- .
- This package is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- .
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
- .
- On Debian systems, the complete text of the GNU General
- Public License version 3 can be found in \"/usr/share/common-licenses/GPL-3\".
-
-Files: debian/rules
-Copyright: 2013 Alin Andrei
-License: MIT
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the \"Software\"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- .
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- .
- THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE."
-
-#postinstall script
-postinst="#!/bin/sh
-set -e
-
-# Work-around Menu item not being created on first installation
-if [ -x /usr/bin/desktop-file-install ]; then
- desktop-file-install /usr/share/applications/butter.desktop > /dev/null 2>&1
-fi
-
-# Work-around for My App not being executable:
-if [ -e /opt/my-app/nw ]; then
- chmod +x /opt/my-app/binary
-fi"
-
-#pre-remove script
-prerm="#!/bin/sh
-set -e
-
-#remove terminal launch
-rm -rf /usr/bin/myapp
-
-#remove symlinked libraries
-rm -rf /opt/my-app/libs"
diff --git a/casks/popcorn-time.rb b/dist/mac/casks/popcorn-time.rb
similarity index 75%
rename from casks/popcorn-time.rb
rename to dist/mac/casks/popcorn-time.rb
index 24dc6b5a02..0a699493c1 100644
--- a/casks/popcorn-time.rb
+++ b/dist/mac/casks/popcorn-time.rb
@@ -1,12 +1,12 @@
cask "popcorn-time" do
- version "0.4.9"
+ version "0.5.0"
nwjs = "0.64.0"
arch = "x64"
name token.gsub(/\b\w/, &:capitalize)
desc "BitTorrent client that includes an integrated media player"
- homepage "https://shows.cf/"
+ homepage "https://github.com/popcorn-official/popcorn-desktop/releases/download/v0.5.0/Popcorn-Time-0.5.0-osx64.zip"
repo = "popcorn-official/popcorn-desktop"
zip = "#{name.first}-#{version}-osx64.zip"
@@ -21,9 +21,9 @@
silent = "silent"
end
- sha256 "773235cce1ff637e3d1dcf5df02413da2eca2198c1d310cc7a4e78afcc4a38ea"
+ sha256 "26abc15d95b4afa48d9383f997ed7393bbcc0cca794a6aa8210b3dc468c08b89"
- url "#{homepage}/build/#{zip}"
+ url "https://github.com/popcorn-official/popcorn-desktop/releases/download/v0.5.0/Popcorn-Time-0.5.0-osx64.zip"
auto_updates true
depends_on arch: :x86_64
diff --git a/CONTRIBUTING.md b/docs/Contributing.md
similarity index 100%
rename from CONTRIBUTING.md
rename to docs/Contributing.md
diff --git a/gulpfile.js b/gulpfile.js
index 45e7c006ae..9983cdb5b5 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -3,7 +3,7 @@
/********
* setup *
********/
-const defaultNwVersion = '0.82.0',
+const defaultNwVersion = '0.86.0',
availablePlatforms = ['linux32', 'linux64', 'win32', 'win64', 'osx64'],
releasesDir = 'build',
nwFlavor = 'sdk';
@@ -28,7 +28,6 @@ const gulp = require('gulp'),
const { detectCurrentPlatform } = require('nw-builder/dist/index.cjs');
-// see: https://shows.cf/version.json
const nwVersion = yargs.argv.nwVersion || defaultNwVersion;
/***********
@@ -371,14 +370,10 @@ gulp.task('mac-pkg', () => {
waitProcess(child).then(() => {
console.log('%s pkg packaged in', platform, path.join(process.cwd(), releasesDir));
- if (pkJson.version === curVersion() && !nwSuffix()) {
- resolve();
- return;
- }
return renameFile(
path.join(process.cwd(), releasesDir),
pkJson.name + '-' + pkJson.version + '.pkg',
- pkJson.name + '-' + curVersion() + nwSuffix() + '.pkg'
+ pkJson.name + '-' + curVersion() + '-osx64' + nwSuffix() + '.pkg'
).then(() => resolve());
}).catch(() => {
console.log('%s failed to package pkg', platform);
@@ -422,6 +417,22 @@ gulp.task('nwjs', () => {
return nw.build();
})
+ .then(() => {
+ return Promise.all(
+ nw.options.platforms.map((platform) => {
+ if (platform.indexOf('linux') === -1) {
+ return null;
+ }
+ const child = spawn('bash', [
+ 'dist/linux/copy-libatomic.sh',
+ releasesDir,
+ pkJson.name,
+ platform
+ ]);
+ return waitProcess(child);
+ })
+ );
+ })
.catch(function(error) {
console.error(error);
});
@@ -584,7 +595,7 @@ gulp.task(
'build',
'compresszip',
'deb',
- 'mac-pkg',
+ // 'mac-pkg',
'nsis',
'cleanForDist',
function(done) {
diff --git a/package.json b/package.json
index b59ccfe89e..49dd51fb9a 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"companyName": "Popcorn Time",
"installIcon": "./src/app/images/popcorntime.ico",
"unInstallIcon": "./src/app/images/butter_uninstall.ico",
- "homepage": "https://shows.cf/",
+ "homepage": "https://popcorn-time.site/",
"bugs": "https://github.com/popcorn-official/popcorn-desktop/issues",
"repository": {
"type": "git",
@@ -11,8 +11,8 @@
},
"license": "GPL-3.0",
"main": "src/app/index.html",
- "version": "0.5.0",
- "releaseName": "Mischief Managed",
+ "version": "0.5.1",
+ "releaseName": "Now.. Bring me that Horizon",
"scripts": {
"build": "gulp build",
"clean": "gulp clean",
@@ -22,7 +22,7 @@
"test": "gulp test",
"postinstall": "patch-package"
},
- "chromium-args": "--enable-node-worker",
+ "chromium-args": "--enable-node-worker --no-sandbox",
"engines": {
"yarn": ">= 1.0.0"
},
@@ -73,12 +73,12 @@
"mkdirp": "*",
"mousetrap": "~1.6.5",
"mv": "2.x.x",
+ "mime": "^3.0.0",
"nedb-promises": "^5.0.3",
"node-tvdb": "^4.1.0",
"opensubtitles-api": "^5.1.2",
"patch-package": "^8.0",
"postinstall-postinstall": "^2.1.0",
- "q": "2.0.3",
"readdirp": "2.x.x",
"request": "2.88.x",
"rimraf": "^3.0.0",
@@ -87,7 +87,7 @@
"send": "^0.17.2",
"socks-proxy-agent": "^6.2.1",
"srt-to-vtt": "^1.1",
- "tar": "4.4.18",
+ "tar": "6.2.1",
"torrentcollection6": "^1.0",
"trakt.tv": "7.x.x",
"trakt.tv-images": "5.x.x",
diff --git a/scripts/release.sh b/scripts/release.sh
deleted file mode 100755
index 6d28c9345a..0000000000
--- a/scripts/release.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/sh
-
-set -e
-CURRENT=$(node -p "require('./package.json').version")
-echo "Current $CURRENT"
-echo "Enter release version: "
-read VERSION
-
-read -p "Releasing $VERSION - are you sure? (y/n)" -n 1 -r
-echo
-if [[ $REPLY =~ ^[Yy]$ ]]
-then
- git add -A
- yarn version --new-version $VERSION
- git push origin master
- git push origin --tags
-fi
\ No newline at end of file
diff --git a/src/app/app.js b/src/app/app.js
index 9bd8bd490f..2b8bd1a8b3 100644
--- a/src/app/app.js
+++ b/src/app/app.js
@@ -144,25 +144,21 @@ App.onBeforeStart = function (options) {
// reset app width when the width is bigger than the available width
if (screen.availWidth < width) {
- win.info('Window too big, resetting width');
width = screen.availWidth;
}
// reset app height when the width is bigger than the available height
if (screen.availHeight < height) {
- win.info('Window too big, resetting height');
height = screen.availHeight;
}
// reset x when the screen width is smaller than the window x-position + the window width
if (x < 0 || x + width > screen.width) {
- win.info('Window out of view, recentering x-pos');
x = Math.round((screen.availWidth - width) / 2);
}
// reset y when the screen height is smaller than the window y-position + the window height
if (y < 0 || y + height > screen.height) {
- win.info('Window out of view, recentering y-pos');
y = Math.round((screen.availHeight - height) / 2);
}
@@ -194,7 +190,7 @@ var initApp = function () {
try {
App.showView(mainWindow);
} catch (e) {
- console.error('Couldn\'t start app: ', e, e.stack);
+ win.error('Couldn\'t start app: ', e, e.stack);
}
if (localStorage.maximized === 'true') {
@@ -240,7 +236,6 @@ var deleteCookies = function () {
if (!result.name) {
result = result[0];
}
- win.debug('cookie removed: ' + result.name + ' ' + result.url);
} else {
win.error('cookie removal failed');
}
@@ -250,7 +245,6 @@ var deleteCookies = function () {
win.cookies.getAll({}, function (cookies) {
if (cookies.length > 0) {
- win.debug('Removing ' + cookies.length + ' cookies...');
for (var i = 0; i < cookies.length; i++) {
removeCookie(cookies[i]);
}
@@ -446,7 +440,6 @@ window.ondragenter = function (e) {
mask.show();
mask.on('dragenter', function (e) {
$('.drop-indicator').show();
- win.debug('Drag init');
});
mask.on('dragover', function (e) {
var showDrag = true;
@@ -457,7 +450,6 @@ window.ondragenter = function (e) {
clearTimeout(timeout);
timeout = setTimeout(function () {
if (!showDrag) {
- win.debug('Drag aborted');
$('.drop-indicator').hide();
$('#drop-mask').hide();
}
@@ -531,26 +523,37 @@ var isVideo = function (file) {
};
var handleVideoFile = function (file) {
+ var vjsPlayer = document.getElementById('video_player');
+ if (vjsPlayer) {
+ videojs(vjsPlayer).dispose();
+ }
+ App.vent.trigger('settings:close');
+ App.vent.trigger('about:close');
+ App.vent.trigger('keyboard:close');
+ App.vent.trigger('stream:stop');
+ App.vent.trigger('player:close');
+ App.vent.trigger('torrentcache:stop');
+ App.vent.trigger('preload:stop');
$('.spinner').show();
// look for local subtitles
var checkSubs = function () {
+ var _dir = file.path.replace(/\\/g, '/');
+ _dir = _dir.substr(0, _dir.lastIndexOf('/'));
var _ext = path.extname(file.name);
- var toFind = file.path.replace(_ext, '.srt');
-
- if (fs.existsSync(path.join(toFind))) {
- return {
- local: path.join(toFind)
- };
- } else {
- return null;
- }
+ var _filename = file.name.replace(_ext, '');
+ var found = null;
+ fs.readdirSync(_dir).forEach(file => {
+ if (file.includes(_filename) && file.endsWith('.srt')) {
+ return found = { local: path.join(_dir, file) };
+ }
+ });
+ return found;
};
// get subtitles from provider
var getSubtitles = function (subdata) {
return new Promise(function (resolve, reject) {
- win.debug('Subtitles data request:', subdata);
var subtitleProvider = App.Config.getProviderForType('subtitle');
@@ -561,7 +564,6 @@ var handleVideoFile = function (file) {
win.info(Object.keys(subs).length + ' subtitles found');
resolve(subs);
} else {
- win.warn('No subtitles returned');
if (Settings.subtitle_language !== 'none') {
App.vent.trigger(
'notification:show',
@@ -673,17 +675,16 @@ var handleVideoFile = function (file) {
if (localsub !== null) {
playObj.defaultSubtitle = 'local';
} else {
- playObj.defaultSubtitle = 'none';
+ playObj.defaultSubtitle = Settings.subtitle_language;
}
resolve(playObj);
})
.catch(function (err) {
- win.warn('trakt.matcher.match error:', err);
var localsub = checkSubs();
if (localsub !== null) {
playObj.defaultSubtitle = 'local';
} else {
- playObj.defaultSubtitle = 'none';
+ playObj.defaultSubtitle = Settings.subtitle_language;
}
if (!playObj.title) {
@@ -699,16 +700,34 @@ var handleVideoFile = function (file) {
$('.spinner').hide();
var localVideo = new Backbone.Model(play); // streamer model
- console.debug(
- 'Trying to play local file',
- localVideo.get('src'),
- localVideo.attributes
- );
- var tmpPlayer = App.Device.Collection.selected.attributes.id;
- App.Device.Collection.setDevice('local');
- App.vent.trigger('stream:ready', localVideo); // start stream
- App.Device.Collection.setDevice(tmpPlayer);
+ win.info('Loading local file:', localVideo.get('videoFile') || localVideo.get('src'));
+
+ const fileName = localVideo.get('src').replace(/\\/g, '/').split('/').pop();
+ var torrentStart = new Backbone.Model({
+ torrent: localVideo,
+ title: fileName,
+ defaultSubtitle: localVideo.defaultSubtitle || Settings.subtitle_language,
+ device: App.Device.Collection.selected,
+ video_file: {
+ name: fileName,
+ size: file.size,
+ index: 0,
+ path: localVideo.get('src')
+ },
+ files: [{
+ name: fileName,
+ size: file.size,
+ index: 0,
+ offset: 0,
+ length: file.size,
+ display: true,
+ done: true,
+ path: localVideo.get('src')
+ }]
+ });
+
+ App.vent.trigger('stream:start', torrentStart, 'local');
$('.eye-info-player, .maximize-icon #maxdllb').hide();
$('.vjs-load-progress').css('width', '100%');
@@ -721,13 +740,13 @@ var handleTorrent = function (torrent) {
} catch (err) {
// The player wasn't running
}
+ Settings.importedTorrent = true;
App.Config.getProviderForType('torrentCache').resolve(torrent);
};
window.ondrop = function (e) {
e.preventDefault();
$('#drop-mask').hide();
- console.debug('Drag completed');
$('.drop-indicator').hide();
var file = e.dataTransfer.files[0];
@@ -826,8 +845,6 @@ nw.App.on('open', function (cmd) {
}
if (file) {
- win.debug('File loaded:', file);
-
if (isVideo(file)) {
var fileModel = {
path: file,
diff --git a/src/app/bootstrap.js b/src/app/bootstrap.js
index bda0764eb8..6ddbaede5e 100644
--- a/src/app/bootstrap.js
+++ b/src/app/bootstrap.js
@@ -3,7 +3,6 @@
App.start();
/* load all the things ! */
- var Q = require('q');
var fs = require('fs');
function loadLocalProviders() {
@@ -18,8 +17,6 @@
return null;
}
- win.info('loading local provider', file);
-
return new Promise((resolve, reject) => {
var script = document.createElement('script');
@@ -28,7 +25,7 @@
script.onload = function() {
script.onload = null;
- win.info('loaded', file);
+ win.info('Loaded local provider:', file);
resolve(file);
};
@@ -41,13 +38,13 @@
}
function loadFromNPM(name, fn) {
- var P = require(name);
- return Q(fn(P));
+ const P = require(name);
+ return Promise.resolve(fn(P));
}
function loadProvidersJSON(fn) {
return pkJson.providers.map(function(providerPath) {
- win.info('loading json', providerPath);
+ win.info('Loaded provider:', providerPath);
return loadFromNPM(`./${providerPath}`, fn);
});
}
@@ -58,7 +55,7 @@
});
return packages.map(function(name) {
- win.info('loading npm', regex, name);
+ win.info('Loaded npm', regex, name);
return loadFromNPM(name, fn);
});
}
@@ -120,8 +117,5 @@
});
return providers;
- })
- .then(function(providers) {
- win.info('loaded', providers);
});
})(window.App);
diff --git a/src/app/common.js b/src/app/common.js
index 56522f51d5..866ffbf237 100644
--- a/src/app/common.js
+++ b/src/app/common.js
@@ -1,317 +1,319 @@
var Common = {},
- torrentHealth = require('webtorrent-health');
+ torrentHealth = require('webtorrent-health');
Common.healthMap = {
- 0: 'bad',
- 1: 'medium',
- 2: 'good',
- 3: 'excellent'
+ 0: 'bad',
+ 1: 'medium',
+ 2: 'good',
+ 3: 'excellent'
};
Common.calcHealth = function (torrent) {
- var seeds = torrent.seed;
- var peers = torrent.peer;
-
- // First calculate the seed/peer ratio
- var ratio = peers > 0 ? (seeds / peers) : seeds;
-
- // Normalize the data. Convert each to a percentage
- // Ratio: Anything above a ratio of 5 is good
- var normalizedRatio = Math.min(ratio / 5 * 100, 100);
- // Seeds: Anything above 30 seeds is good
- var normalizedSeeds = Math.min(seeds / 30 * 100, 100);
-
- // Weight the above metrics differently
- // Ratio is weighted 60% whilst seeders is 40%
- var weightedRatio = normalizedRatio * 0.6;
- var weightedSeeds = normalizedSeeds * 0.4;
- var weightedTotal = weightedRatio + weightedSeeds;
-
- // Scale from [0, 100] to [0, 3]. Drops the decimal places
- var scaledTotal = ((weightedTotal * 3) / 100) | 0;
-
- return scaledTotal;
+ var seeds = torrent.seed;
+ var peers = torrent.peer;
+ // First calculate the seed/peer ratio
+ var ratio = peers > 0 ? (seeds / peers) : seeds;
+ // Normalize the data. Convert each to a percentage
+ // Ratio: Anything above a ratio of 5 is good
+ var normalizedRatio = Math.min(ratio / 5 * 100, 100);
+ // Seeds: Anything above 30 seeds is good
+ var normalizedSeeds = Math.min(seeds / 30 * 100, 100);
+ // Weight the above metrics differently
+ // Ratio is weighted 60% whilst seeders is 40%
+ var weightedRatio = normalizedRatio * 0.6;
+ var weightedSeeds = normalizedSeeds * 0.4;
+ var weightedTotal = weightedRatio + weightedSeeds;
+ // Scale from [0, 100] to [0, 3]. Drops the decimal places
+ var scaledTotal = ((weightedTotal * 3) / 100) | 0;
+ return scaledTotal;
};
Common.calcRatio = function (seeds, peers) {
- if (isNaN(seeds) || isNaN(peers)) {
- return NaN;
- }
-
- if (peers > 0) {
- return seeds / peers;
- }
-
- return +seeds;
+ if (isNaN(seeds) || isNaN(peers)) {
+ return NaN;
+ }
+ if (peers > 0) {
+ return seeds / peers;
+ }
+ return +seeds;
};
Common.retrieveTorrentHealth = function (torrent, cb) {
- const torrentURL = typeof torrent === 'string'
- ? torrent
- : torrent.magnet || torrent.url || torrent.magnetURI;
-
- if (!torrentURL) {
- cb(new Error('Torrent URL could not be obtained'), null);
- }
-
- // check for 'magnet:?' because api sometimes sends back links, not magnets
- if (!torrentURL.startsWith('magnet:?')) {
- return cb(new Error('Torrent is not a magnet URL'), null);
- }
-
- torrentHealth(
- torrentURL,
- {
- timeout: 2500,
- trackers: Settings.trackers.forced
- },
- cb
- );
+ const torrentURL = typeof torrent === 'string'
+ ? torrent
+ : torrent.magnet || torrent.url || torrent.magnetURI;
+ if (!torrentURL) {
+ cb(new Error('Torrent URL could not be obtained'), null);
+ }
+ // check for 'magnet:?' because api sometimes sends back links, not magnets
+ if (!torrentURL.startsWith('magnet:?')) {
+ return cb(new Error('Torrent is not a magnet URL'), null);
+ }
+ torrentHealth(
+ torrentURL,
+ {
+ timeout: 2500,
+ trackers: Settings.trackers.forced
+ },
+ cb
+ );
};
Common.HealthButton = function (selector, retrieveHealthCallback) {
- if (!(this instanceof Common.HealthButton)) {
- throw new TypeError('This class must be constructed with "new"');
- }
-
- const maxChecksWhenNoSeeds = 3;
- let zeroSeedCheckCount = 0;
- let pendingRender = null;
-
- const getIcon = () => {
- return $(selector);
- };
-
- this.reset = () => {
- getIcon()
- .tooltip({
- html: true
- })
- .removeClass('Bad Medium Good Excellent')
- .addClass('None')
- .attr('data-original-title', i18n.__('Health Unknown'))
- .tooltip('fixTitle');
- };
-
- this.cancelPendingRenders = () => {
- if (pendingRender) {
- pendingRender.isCancelled = true;
- }
- };
-
- this.render = () => {
- // because this is an object, we can keep a ref to it while
- // allowing other callers to modify it outside of the current
- // scope. this lets us know if anyone outside wants this
- // request to be cancelled
- const cancellationLock = {isCancelled: false};
- this.cancelPendingRenders();
- pendingRender = cancellationLock;
-
- retrieveHealthCallback((err, res) => {
- if (err || cancellationLock.isCancelled) {
- return;
- }
-
- const seeds = Math.max.apply(Math, res.extra.map(function(o) { return o.seeds || 0; }));
- const peers = Math.max.apply(Math, res.extra.map(function(o) { return o.peers || 0; }));
- win.debug('torrent health:', res);
-
- if (seeds === 0 && zeroSeedCheckCount < maxChecksWhenNoSeeds) {
- zeroSeedCheckCount++;
- getIcon().click();
- } else {
- zeroSeedCheckCount = 0;
- const healthValue = Common.calcHealth({seed: seeds, peer: peers});
- const healthString = Common.healthMap[healthValue].capitalize();
- const ratio = res.ratio || Common.calcRatio(seeds, peers);
-
- const tooltipPieces = [
- i18n.__(`Health ${healthString}`)
- ];
-
- if (!isNaN(ratio)) {
- tooltipPieces.push(` - ${i18n.__('Ratio:')} ${ratio.toFixed(2)}
`);
- }
-
- if (!isNaN(seeds)) {
- tooltipPieces.push(`${i18n.__('Seeds:')} ${seeds}`);
- }
-
- if (!isNaN(peers)) {
- tooltipPieces.push(` - ${i18n.__('Peers:')} ${peers}`);
- }
-
- getIcon()
- .tooltip({
- html: true
- })
- .removeClass('None Bad Medium Good Excellent')
- .addClass(healthString)
- .attr('data-original-title', tooltipPieces.join(''))
- .tooltip('fixTitle');
-
- if ($(selector + '~ .tooltip:contains("Health")').is(':visible')) {
- getIcon().tooltip('show');
- }
- }
- });
- };
+ if (!(this instanceof Common.HealthButton)) {
+ throw new TypeError('This class must be constructed with "new"');
+ }
+ const maxChecksWhenNoSeeds = 3;
+ let zeroSeedCheckCount = 0;
+ let pendingRender = null;
+ const getIcon = () => {
+ return $(selector);
+ };
+ this.reset = () => {
+ getIcon()
+ .tooltip({
+ html: true
+ })
+ .removeClass('Bad Medium Good Excellent')
+ .addClass('None')
+ .attr('data-original-title', i18n.__('Health Unknown'))
+ .tooltip('fixTitle');
+ };
+ this.cancelPendingRenders = () => {
+ if (pendingRender) {
+ pendingRender.isCancelled = true;
+ }
+ };
+ this.render = () => {
+ // because this is an object, we can keep a ref to it while
+ // allowing other callers to modify it outside of the current
+ // scope. this lets us know if anyone outside wants this
+ // request to be cancelled
+ const cancellationLock = {isCancelled: false};
+ this.cancelPendingRenders();
+ pendingRender = cancellationLock;
+ retrieveHealthCallback((err, res) => {
+ if (err || cancellationLock.isCancelled) {
+ return;
+ }
+ const seeds = Math.max.apply(Math, res.extra.map(function(o) { return o.seeds || 0; }));
+ const peers = Math.max.apply(Math, res.extra.map(function(o) { return o.peers || 0; }));
+ if (seeds === 0 && zeroSeedCheckCount < maxChecksWhenNoSeeds) {
+ zeroSeedCheckCount++;
+ getIcon().click();
+ } else {
+ zeroSeedCheckCount = 0;
+ const healthValue = Common.calcHealth({seed: seeds, peer: peers});
+ const healthString = Common.healthMap[healthValue].capitalize();
+ const ratio = res.ratio || Common.calcRatio(seeds, peers);
+ const tooltipPieces = [
+ i18n.__(`Health ${healthString}`)
+ ];
+ if (!isNaN(ratio)) {
+ tooltipPieces.push(` - ${i18n.__('Ratio:')} ${ratio.toFixed(2)}
`);
+ }
+ if (!isNaN(seeds)) {
+ tooltipPieces.push(`${i18n.__('Seeds:')} ${seeds}`);
+ }
+ if (!isNaN(peers)) {
+ tooltipPieces.push(` / ${i18n.__('Peers:')} ${peers}`);
+ }
+ getIcon()
+ .tooltip({
+ html: true
+ })
+ .removeClass('None Bad Medium Good Excellent')
+ .addClass(healthString)
+ .attr('data-original-title', tooltipPieces.join(''))
+ .tooltip('fixTitle');
+ if ($(selector + '~ .tooltip:contains("Health")').is(':visible')) {
+ getIcon().tooltip('show');
+ }
+ }
+ });
+ };
};
Common.md5 = function (arg) {
- return crypt.createHash('md5').update(arg).digest('hex');
+ return crypt.createHash('md5').update(arg).digest('hex');
};
Common.fileSize = function (num) {
- if (isNaN(num) || num === null) {
- return;
- }
-
- num = parseInt(num);
-
- var exponent, unit, units, base;
- var neg = num < 0;
-
- switch (os.platform()) {
- case 'linux':
- base = 1024;
- units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
- break;
- case 'win32':
- base = 1024;
- units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
- break;
- case 'darwin':
- /* falls through */
- default:
- base = 1000;
- units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
- }
-
- if (neg) {
- num = -num;
- }
-
- if (num < 1) {
- unit = units[0];
- if (Settings.language === 'fr') {
- unit = unit.replace('B', 'o');
- }
- return (neg ? '-' : '') + num + ' ' + unit;
- }
-
- exponent = Math.min(Math.floor(Math.log(num) / Math.log(base)), units.length - 1);
- num = (num / Math.pow(base, exponent)).toFixed(2) * 1;
- unit = units[exponent];
-
- var matcher = Settings.language.match(/sq|es|hy|az|be|qu|pt|bs|ca|bg|hr|cs|da|et|fo|fi|fr|de|ka|el|hu|is|id|it|kk|lv|lt|mn|nl|nn|nb|no|pl|ro|ru|sr|sk|sl|sv|tr|uk|uz|vi/);
- if (matcher !== null) {
- num = num.toString().replace('.', ',');
- }
- if (Settings.language === 'fr') {
- unit = unit.replace('B', 'o');
- }
- return (neg ? '-' : '') + num + ' ' + unit;
+ if (isNaN(num) || num === null) {
+ return;
+ }
+ num = parseInt(num);
+ var exponent, unit, units, base;
+ var neg = num < 0;
+ switch (os.platform()) {
+ case 'linux':
+ base = 1024;
+ units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
+ break;
+ case 'win32':
+ base = 1024;
+ units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+ break;
+ case 'darwin':
+ /* falls through */
+ default:
+ base = 1000;
+ units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+ }
+ if (neg) {
+ num = -num;
+ }
+ if (num < 1) {
+ unit = units[0];
+ if (Settings.language === 'fr') {
+ unit = unit.replace('B', 'o');
+ }
+ return (neg ? '-' : '') + num + ' ' + unit;
+ }
+ exponent = Math.min(Math.floor(Math.log(num) / Math.log(base)), units.length - 1);
+ num = (num / Math.pow(base, exponent)).toFixed(2) * 1;
+ unit = units[exponent];
+ var matcher = Settings.language.match(/sq|es|hy|az|be|qu|pt|bs|ca|bg|hr|cs|da|et|fo|fi|fr|de|ka|el|hu|is|id|it|kk|lv|lt|mn|nl|nn|nb|no|pl|ro|ru|sr|sk|sl|sv|tr|uk|uz|vi/);
+ if (matcher !== null) {
+ num = num.toString().replace('.', ',');
+ }
+ if (Settings.language === 'fr') {
+ unit = unit.replace('B', 'o');
+ }
+ return (neg ? '-' : '') + num + ' ' + unit;
};
Common.sanitize = function (input) {
- function sanitizeString(string) {
- return require('sanitizer').sanitize(string);
- }
-
- function sanitizeObject(obj) {
- var result = obj;
- for (var prop in obj) {
- result[prop] = obj[prop];
- if (obj[prop] && (obj[prop].constructor === Object || obj[prop].constructor === Array)) {
- result[prop] = sanitizeObject(obj[prop]);
- } else if (obj[prop] && obj[prop].constructor === String) {
- result[prop] = sanitizeString(obj[prop]);
- }
- }
- return result;
- }
-
- var output = input;
- if (input && (input.constructor === Object || input.constructor === Array)) {
- output = sanitizeObject(input);
- } else if (input && input.constructor === String) {
- output = sanitizeString(input);
- }
-
- return output;
+ function sanitizeString(string) {
+ return require('sanitizer').sanitize(string);
+ }
+ function sanitizeObject(obj) {
+ var result = obj;
+ for (var prop in obj) {
+ result[prop] = obj[prop];
+ if (obj[prop] && (obj[prop].constructor === Object || obj[prop].constructor === Array)) {
+ result[prop] = sanitizeObject(obj[prop]);
+ } else if (obj[prop] && obj[prop].constructor === String) {
+ result[prop] = sanitizeString(obj[prop]);
+ }
+ }
+ return result;
+ }
+ var output = input;
+ if (input && (input.constructor === Object || input.constructor === Array)) {
+ output = sanitizeObject(input);
+ } else if (input && input.constructor === String) {
+ output = sanitizeString(input);
+ }
+ return output;
};
Common.normalize = (function () {
- var from = 'ÃÀÁÄÂÈÉËÊÌÍÏÎÒÓÖÔÙÚÜÛãàáäâèéëêìíïîòóöôùúüûÑñÇç';
- var to = 'AAAAAEEEEIIIIOOOOUUUUaaaaaeeeeiiiioooouuuunncc';
- var mapping = {};
-
- for (var i = 0, j = from.length; i < j; i++) {
- mapping[from.charAt(i)] = to.charAt(i);
- }
-
- return function (str) {
- var ret = [];
- for (var i = 0, j = str.length; i < j; i++) {
- var c = str.charAt(i);
- if (mapping.hasOwnProperty(str.charAt(i))) {
- ret.push(mapping[c]);
- } else {
- ret.push(c);
- }
- }
- return ret.join('').replace(/[^\w,'-]/g, '');
- };
+ var from = 'ÃÀÁÄÂÈÉËÊÌÍÏÎÒÓÖÔÙÚÜÛãàáäâèéëêìíïîòóöôùúüûÑñÇç';
+ var to = 'AAAAAEEEEIIIIOOOOUUUUaaaaaeeeeiiiioooouuuunncc';
+ var mapping = {};
+ for (var i = 0, j = from.length; i < j; i++) {
+ mapping[from.charAt(i)] = to.charAt(i);
+ }
+ return function (str) {
+ var ret = [];
+ for (var i = 0, j = str.length; i < j; i++) {
+ var c = str.charAt(i);
+ if (mapping.hasOwnProperty(str.charAt(i))) {
+ ret.push(mapping[c]);
+ } else {
+ ret.push(c);
+ }
+ }
+ return ret.join('').replace(/[^\w,'-]/g, '');
+ };
})();
-Common.loadImage = function(img) {
- return new Promise(function(resolve, reject) {
- let cache = new Image();
- cache.onload = () => {
- if (img.indexOf('.gif') !== -1) {
- // freeze gifs
- let c = document.createElement('canvas');
- let w = (c.width = img.width);
- let h = (c.height = img.height);
-
- c.getContext('2d').drawImage(cache, 0, 0, w, h);
- img = c.toDataURL();
- }
- resolve(img);
- };
-
- cache.onerror = () => resolve(null);
- cache.src = img;
- });
+Common.loadImage = function(img, proxy = false) {
+ return new Promise(function(resolve, reject) {
+ let cache = new Image();
+ cache.onload = () => {
+ if (img.indexOf('.gif') !== -1) {
+ // freeze gifs
+ let c = document.createElement('canvas');
+ let w = (c.width = img.width);
+ let h = (c.height = img.height);
+ c.getContext('2d').drawImage(cache, 0, 0, w, h);
+ img = c.toDataURL();
+ }
+ resolve(img);
+ };
+ cache.onerror = () => {
+ if (proxy || img.indexOf('image.tmdb.org') === -1) {
+ resolve(null);
+ return;
+ }
+ const apiUrl = App.Config.getProviderForType('tvshow')[0].apiURL;
+ const url = apiUrl[0] + 'posters/' + img.split('/').pop();
+ resolve(Common.loadImage(url, true));
+ };
+ cache.src = img;
+ });
};
Common.Promises = {
- allSettled: function (promises) {
- var wrappedPromises = promises.map(
- p => Promise.resolve(p)
- .then(val => ({ok: true, value: val}), err => ({ok: false, reason: err})
- ));
- return Promise.all(wrappedPromises);
- }
+ allSettled: function (promises) {
+ var wrappedPromises = promises.map(
+ p => Promise.resolve(p)
+ .then(val => ({ok: true, value: val}), err => ({ok: false, reason: err})
+ ));
+ return Promise.all(wrappedPromises);
+ }
};
Common.getTorrentUri = torrent => torrent.magnet || torrent.url || torrent;
Common.openOrClipboardLink = function(e, link, text, noOpen = false, noCopy = false) {
- if (e.button === 2 && !noCopy) {
- var clipboard = nw.Clipboard.get();
- clipboard.set(link, 'text');
- $('.notification_alert')
- .text(i18n.__('The %s was copied to the clipboard', text))
- .fadeIn('fast')
- .delay(2500)
- .fadeOut('fast')
- ;
- }
- if (e.button === 0 && !noOpen) {
- nw.Shell.openExternal(link);
- }
+ if (e.button === 2 && !noCopy) {
+ var clipboard = nw.Clipboard.get();
+ clipboard.set(link, 'text');
+ $('.notification_alert')
+ .text(i18n.__('The %s was copied to the clipboard', text))
+ .fadeIn('fast')
+ .delay(2500)
+ .fadeOut('fast')
+ ;
+ }
+ if (e.button === 0 && !noOpen) {
+ nw.Shell.openExternal(link);
+ }
+};
+
+Common.selectPlayer = function(e, thisModel) {
+ var player = $(e.currentTarget).parent('li').attr('id').replace('player-', '');
+ thisModel.set('device', player);
+ if (!player.match(/[0-9]+.[0-9]+.[0-9]+.[0-9]/ig)) {
+ AdvSettings.set('chosenPlayer', player);
+ }
+};
+
+Common.showPlayerList = function() {
+ App.vent.trigger('notification:show', new App.Model.Notification({
+ title: '',
+ body: i18n.__('Popcorn Time currently supports') + '' + extPlayerlst + '.
' + i18n.__('There is also support for Chromecast, AirPlay & DLNA devices.'),
+ type: 'success'
+ }));
+};
+
+Common.refreshPlayerList = function (e) {
+ e.stopPropagation();
+ const dropdownToggle = '.' + $(e.currentTarget).parents().eq(4)[0].className + ' .playerchoice';
+ $('.playerchoicerefresh').addClass('fa-spin fa-spinner spin').tooltip('hide');
+ Promise.all(App.Device.loadDeviceSupport()).then(function(data) {
+ App.Device.rescan();
+ }).then(function() {
+ setTimeout(() => {
+ App.Device.ChooserView('#player-chooser').render();
+ App.Device.ChooserView('#player-chooser2').render();
+ $('.file-selector #watch-now').text('');
+ $('.playerchoicerefresh, .playerchoicehelp').tooltip({html: true, delay: {'show': 800,'hide': 100}});
+ $(dropdownToggle).click();
+ }, 3000);
+ });
};
Common.qualityCollator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
diff --git a/src/app/lib/config.js b/src/app/config.js
similarity index 100%
rename from src/app/lib/config.js
rename to src/app/config.js
diff --git a/src/app/css/animation.css b/src/app/css/animation.css
deleted file mode 100644
index 9312c73d24..0000000000
--- a/src/app/css/animation.css
+++ /dev/null
@@ -1,64 +0,0 @@
-#init-content {
- width: 100%;
- /* Full Width */
- height: 5px;
- background: #000;
- margin-top: 14%;
-}
-
-.init-expand {
- width: 100%;
- height: 1px;
- margin: 2px 0;
- background: #2187e7;
- position: absolute;
- box-shadow: 0px 0px 10px 1px rgba(0, 198, 255, 0.7);
- -moz-animation: fullexpand 10s ease-out;
- -webkit-animation: fullexpand 10s ease-out;
-}
-
-/* Full Width Animation Bar */
-
-@-webkit-keyframes fullexpand {
- 0% {
- width: 0px;
- }
-
- 100% {
- width: 100%;
- }
-}
-
-@-webkit-keyframes spin {
- 0% {
- -webkit-transform: rotate(0deg);
- }
- 100% {
- -webkit-transform: rotate(360deg);
- }
-}
-
-@-webkit-keyframes spinoff {
- 0% {
- -webkit-transform: rotate(0deg);
- }
- 100% {
- -webkit-transform: rotate(-360deg);
- }
-}
-
-@keyframes spin {
- 0% {
- transform: rotate(0deg);
- }
-
- 100% {
- transform: rotate(359deg);
- }
-}
-
-@keyframes blinker {
- 50% {
- opacity: 0;
- }
-}
diff --git a/src/app/database.js b/src/app/database.js
index d85c7dffb0..6a5962f850 100644
--- a/src/app/database.js
+++ b/src/app/database.js
@@ -3,7 +3,6 @@ var Datastore = require('nedb-promises'),
TTL = 1000 * 60 * 60 * 24;
var startupTime = window.performance.now();
-console.debug('Database path: ' + data_path);
db.bookmarks = new Datastore({
filename: path.join(data_path, 'data/bookmarks.db'),
@@ -93,15 +92,15 @@ var Database = {
},
deleteBookmarks: function () {
- return db.bookmarks.remove({}, {
- multi: true
- });
+ try { fs.unlinkSync(path.join(data_path, 'data/movies.db')); } catch (error) {}
+ try { fs.unlinkSync(path.join(data_path, 'data/shows.db')); } catch (error) {}
+ try { fs.unlinkSync(path.join(data_path, 'data/bookmarks.db')); } catch (error) {}
+ return Promise.resolve();
},
deleteWatched: function () {
- return db.watched.remove({}, {
- multi: true
- });
+ try { fs.unlinkSync(path.join(data_path, 'data/watched.db')); } catch (error) {}
+ return Promise.resolve();
},
// format: {page: page, keywords: title}
@@ -334,9 +333,8 @@ var Database = {
},
resetSettings: function () {
- return db.settings.remove({}, {
- multi: true
- });
+ try { fs.unlinkSync(path.join(data_path, 'data/settings.db')); } catch (error) {}
+ return Promise.resolve();
},
applyDhtSettings: function (dhtInfo) {
@@ -351,12 +349,10 @@ var Database = {
Settings.issuesUrl = dhtInfo.git + 'issues';
Settings.sourceUrl = dhtInfo.git;
Settings.commitUrl = dhtInfo.git + 'commit';
- Settings.projectCi = dhtInfo.git + 'actions';
Settings.projectBlog = dhtInfo.git + 'wiki';
}
if (dhtInfo.site) {
Settings.projectUrl = dhtInfo.site;
- dhtInfo.d ? Settings.projectForum2 = dhtInfo.site.split('//')[0] + '//discuss.' + dhtInfo.site.split('//')[1] : null;
dhtInfo.s ? Settings.statusUrl = dhtInfo.site.split('//')[0] + '//status.' + dhtInfo.site.split('//')[1] : null;
}
if (dhtInfo.keys) {
@@ -372,17 +368,11 @@ var Database = {
},
deleteDatabases: function () {
-
- fs.unlinkSync(path.join(data_path, 'data/watched.db'));
-
- fs.unlinkSync(path.join(data_path, 'data/movies.db'));
-
- fs.unlinkSync(path.join(data_path, 'data/bookmarks.db'));
-
- fs.unlinkSync(path.join(data_path, 'data/shows.db'));
-
- fs.unlinkSync(path.join(data_path, 'data/settings.db'));
-
+ try { fs.unlinkSync(path.join(data_path, 'data/watched.db')); } catch (error) {}
+ try { fs.unlinkSync(path.join(data_path, 'data/movies.db')); } catch (error) {}
+ try { fs.unlinkSync(path.join(data_path, 'data/bookmarks.db')); } catch (error) {}
+ try { fs.unlinkSync(path.join(data_path, 'data/shows.db')); } catch (error) {}
+ try { fs.unlinkSync(path.join(data_path, 'data/settings.db')); } catch (error) {}
return Promise.resolve();
},
@@ -449,7 +439,7 @@ var Database = {
})
.then(function () {
if (AdvSettings.get('disclaimerAccepted')) {
- App.DhtReader.updateOld();
+ App.Updater.updateDHTOld();
if (Settings.updateNotification) {
App.Updater.onlyNotification();
}
diff --git a/src/app/dht.js b/src/app/dht.js
deleted file mode 100644
index c0bda2764d..0000000000
--- a/src/app/dht.js
+++ /dev/null
@@ -1,152 +0,0 @@
-'use strict';
-
-var DHT = require('bittorrent-dht');
-var ed = require('@noble/ed25519'); // better use ed25519-supercop but need rebuild ed25519 for windows
-
-class DhtReader {
- constructor(options) {
- this.options = _.defaults(options || {}, {});
- }
-
- update(e) {
- const self = this;
- if (!Settings.dht) {
- if (e && e !== 'urls') {
- self.alertIcon('error');
- self.alertMessage('error');
- }
- return;
- } else if (e && e !== 'urls') {
- self.alertIcon();
- self.alertMessage('wait');
- }
- const dht = new DHT({verify: ed.verify});
- const hash = Buffer(Settings.dht, 'hex');
- dht.once('ready', function () {
- dht.get(hash, function (err, node) {
- if (err || !node || !node.v) {
- if (e && e !== 'urls') {
- self.alertIcon('error');
- self.alertMessage('error');
- }
- return;
- }
- let data = AdvSettings.get('dhtData');
- let newData = node.v.toString();
- let info = AdvSettings.get('dhtInfo');
- let newInfo = typeof newData === 'string' ? JSON.parse(newData) : null;
- AdvSettings.set('dhtData', newData);
- AdvSettings.set('dhtDataUpdated', Date.now());
- if (e !== 'urls'){
- if (e) {
- self.alertIcon('success');
- }
- AdvSettings.set('dhtInfo', newInfo);
- }
- if (data !== newData && e !== 'urls') {
- self.updateSettings();
- if (!Settings.dhtEnable || (Settings.customMoviesServer || Settings.customSeriesServer || Settings.customAnimeServer)) {
- self.alertMessage('change');
- } else {
- self.alertMessage('restart');
- }
- } else if (e === 'enable') {
- self.alertMessage('restart');
- } else if (e === 'manual') {
- if (info.toString() !== newInfo.toString()) {
- self.alertMessage('restart');
- } else {
- self.alertMessage('alrdupdated');
- }
- }
- });
- });
- }
-
- updateOld() {
- if (!Settings.dht) {
- return;
- }
- let data = AdvSettings.get('dhtData');
- let last = AdvSettings.get('dhtDataUpdated');
- const time = 1000 * 60 * 60 * 24 * 7;
- if (!data) {
- if (Settings.dhtEnable) {
- this.update('enable');
- } else {
- this.update('urls');
- }
- } else if (Date.now() - last > time) {
- this.update();
- }
- }
-
- updateSettings() {
- setTimeout(function() {
- if (App.ViewStack.includes('settings-container-contain')) {
- let scrollPos = $('.settings-container-contain').scrollTop();
- $('.nav-hor.left li:first').click();
- App.vent.trigger('settings:show');
- $('.update-dht').removeClass('fa-spin fa-spinner').addClass('valid-tick');
- $('.settings-container-contain').scrollTop(scrollPos);
- }
- }, 200);
- }
-
- alertIcon(e) {
- if (e) {
- let tmpclass = e === 'success' ? 'valid-tick' : 'invalid-cross';
- $('.update-dht').removeClass('fa-spin fa-spinner').addClass(tmpclass);
- setTimeout(function() { $('.update-dht').removeClass(tmpclass).addClass('fa-rotate');}, 6000);
- } else {
- $('.update-dht').removeClass('fa-rotate valid-tick invalid-cross').addClass('fa-spin fa-spinner');
- }
- }
-
- alertMessage(alertType) {
- var changeServer = function () {
- let newServer = AdvSettings.get('dhtData') && !AdvSettings.get('dhtEnable') ? Settings.dhtInfo.server : '';
- AdvSettings.set('customMoviesServer', newServer);
- AdvSettings.set('customSeriesServer', newServer);
- AdvSettings.set('customAnimeServer', newServer);
- this.alertMessage('restart');
- }.bind(this);
- var notificationModel = new App.Model.Notification({
- title: i18n.__('Success'),
- showClose: false,
- type: 'success',
- });
- switch (alertType) {
- case 'wait':
- notificationModel.set('title', i18n.__('Please wait') + '...');
- notificationModel.set('body', i18n.__('Updating the API Server URLs'));
- notificationModel.set('type', 'danger');
- break;
- case 'error':
- notificationModel.set('title', i18n.__('Error'));
- notificationModel.set('body', i18n.__('API Server URLs could not be updated'));
- notificationModel.set('type', 'error');
- notificationModel.set('autoclose', true);
- break;
- case 'change':
- notificationModel.set('body', i18n.__('Change API Server(s) to the new URLs?'));
- notificationModel.set('buttons', [{ title: '', action: changeServer }, { title: '', action: function () {this.alertMessage('updated');}.bind(this)}]);
- break;
- case 'restart':
- notificationModel.set('body', i18n.__('Please restart your application'));
- notificationModel.set('showRestart', true);
- notificationModel.set('showClose', true);
- break;
- case 'updated':
- notificationModel.set('body', i18n.__('API Server URLs updated'));
- notificationModel.set('autoclose', true);
- break;
- case 'alrdupdated':
- notificationModel.set('body', i18n.__('API Server URLs already updated'));
- notificationModel.set('autoclose', true);
- }
- App.vent.trigger('notification:show', notificationModel);
- }
-}
-
-App.DhtReader = new DhtReader();
diff --git a/src/app/fileserver.js b/src/app/fileserver.js
new file mode 100644
index 0000000000..aa8a5d04d0
--- /dev/null
+++ b/src/app/fileserver.js
@@ -0,0 +1,258 @@
+const http = require('http');
+const escapeHtml = require('escape-html');
+const mime = require('mime');
+const pump = require('pump');
+const rangeParser = require('range-parser');
+const queueMicrotask = require('queue-microtask');
+const fs = require('fs'); // we only need fs to get the ReadStream and WriteStream prototypes
+
+function FileServer (file, opts = {}) {
+ const server = http.createServer();
+ if (!opts.origin) { opts.origin = '*'; } // allow all origins by default
+
+ const sockets = new Set();
+ let closed = false;
+ const _listen = server.listen;
+ const _close = server.close;
+
+ server.listen = (...args) => {
+ closed = false;
+ server.on('connection', onConnection);
+ server.on('request', onRequest);
+ return _listen.apply(server, args);
+ };
+
+ server.close = cb => {
+ closed = true;
+ server.removeListener('connection', onConnection);
+ server.removeListener('request', onRequest);
+ _close.call(server, cb);
+ };
+
+ server.destroy = cb => {
+ sockets.forEach(socket => {
+ socket.destroy();
+ });
+
+ // Only call `server.close` if user has not called it already
+ if (!cb) { cb = () => {};}
+ if (closed) { queueMicrotask(cb); }
+ else { server.close(cb); }
+ file = null;
+ };
+
+ function isOriginAllowed (req) {
+ // When `origin` option is `false`, deny all cross-origin requests
+ if (opts.origin === false) return false;
+
+ // Requests without an 'Origin' header are not actually cross-origin, so just
+ // deny them
+ if (req.headers.origin == null) return false;
+
+ // The user allowed all origins
+ if (opts.origin === '*') return true;
+
+ // Allow requests where the 'Origin' header matches the `opts.origin` setting
+ return req.headers.origin === opts.origin;
+ }
+
+ function onConnection (socket) {
+ socket.setTimeout(36000000);
+ sockets.add(socket);
+ socket.once('close', () => {
+ sockets.delete(socket);
+ });
+ }
+
+ function onRequest (req, res) {
+ // If a 'hostname' string is specified, deny requests with a 'Host'
+ // header that does not match the origin of the torrent server to prevent
+ // DNS rebinding attacks.
+ if (opts.hostname && req.headers.host !== `${opts.hostname}:${server.address().port}`) {
+ return req.destroy();
+ }
+
+ const pathname = new URL(req.url, 'http://example.com').pathname;
+
+ // Allow cross-origin requests (CORS)
+ if (isOriginAllowed(req)) {
+ res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
+ }
+
+ // Prevent browser mime-type sniffing
+ res.setHeader('X-Content-Type-Options', 'nosniff');
+
+ // Defense-in-depth: Set a strict Content Security Policy to mitigate XSS
+ res.setHeader('Content-Security-Policy', "base-uri 'none'; default-src 'none'; frame-ancestors 'none'; form-action 'none';");
+
+ if (pathname === '/favicon.ico') {
+ return serve404Page();
+ }
+
+ // Allow CORS requests to specify arbitrary headers, e.g. 'Range',
+ // by responding to the OPTIONS preflight request with the specified
+ // origin and requested headers.
+ if (req.method === 'OPTIONS') {
+ if (isOriginAllowed(req)) return serveOptionsRequest();
+ else return serveMethodNotAllowed();
+ }
+
+ if (req.method === 'GET' || req.method === 'HEAD') {
+ return handleRequest();
+ }
+
+ return serveMethodNotAllowed();
+
+ function serveOptionsRequest () {
+ res.statusCode = 204 // no content
+ res.setHeader('Access-Control-Max-Age', '600')
+ res.setHeader('Access-Control-Allow-Methods', 'GET,HEAD')
+
+ if (req.headers['access-control-request-headers']) {
+ res.setHeader(
+ 'Access-Control-Allow-Headers',
+ req.headers['access-control-request-headers']
+ )
+ }
+ res.end()
+ }
+
+ function handleRequest () {
+ if (pathname === '/') {
+ return serveIndexPage();
+ }
+
+ const index = Number(pathname.split('/')[1]);
+ if (Number.isNaN(index)) {
+ return serve404Page();
+ }
+
+ serveFile(file);
+ }
+
+ function serveIndexPage () {
+ res.statusCode = 200;
+ res.setHeader('Content-Type', 'text/html');
+
+ const listHtml =
+ `
+
+ ${escapeHtml(file.path)}
+
+ (${escapeHtml(file.length)} bytes)
+ `;
+
+ const html = getPageHTML(
+ `Local File - WebTorrent`,
+ `
+ Local File
+ ${listHtml}
+ `
+ );
+ res.end(html);
+ }
+
+ function serve404Page () {
+ res.statusCode = 404;
+ res.setHeader('Content-Type', 'text/html');
+
+ const html = getPageHTML(
+ '404 - Not Found',
+ '404 - Not Found
'
+ );
+ res.end(html);
+ }
+
+ function serveFile (file) {
+ res.setHeader('Content-Type', mime.getType(file.name) || 'application/octet-stream');
+
+ // Support range-requests
+ res.setHeader('Accept-Ranges', 'bytes');
+
+ // Set name of file (for "Save Page As..." dialog)
+ res.setHeader(
+ 'Content-Disposition',
+ `inline; filename*=UTF-8''${encodeRFC5987(file.name)}`
+ );
+
+ // Support DLNA streaming
+ res.setHeader('transferMode.dlna.org', 'Streaming');
+ res.setHeader(
+ 'contentFeatures.dlna.org',
+ 'DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000'
+ );
+
+ // `rangeParser` returns an array of ranges, or an error code (number) if
+ // there was an error parsing the range.
+ let range = rangeParser(file.length, req.headers.range || '');
+
+ if (Array.isArray(range)) {
+ res.statusCode = 206 // indicates that range-request was understood
+
+ // no support for multi-range request, just use the first range
+ range = range[0]
+
+ res.setHeader(
+ 'Content-Range',
+ `bytes ${range.start}-${range.end}/${file.length}`
+ )
+ res.setHeader('Content-Length', range.end - range.start + 1)
+ } else {
+ res.statusCode = 200
+ range = null
+ res.setHeader('Content-Length', file.length)
+ }
+
+ if (req.method === 'HEAD') {
+ return res.end()
+ }
+
+ pump(fs.createReadStream(file.path, range), res);
+ }
+
+ function serveMethodNotAllowed () {
+ res.statusCode = 405
+ res.setHeader('Content-Type', 'text/html')
+ const html = getPageHTML(
+ '405 - Method Not Allowed',
+ '405 - Method Not Allowed
'
+ )
+ res.end(html)
+ }
+ }
+
+ return server
+}
+
+// NOTE: Arguments must already be HTML-escaped
+function getPageHTML (title, pageHtml) {
+ return `
+
+
+
+
+ ${title}
+
+
+ ${pageHtml}
+
+
+ `;
+}
+
+// From https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
+function encodeRFC5987 (str) {
+ return encodeURIComponent(str)
+ // Note that although RFC3986 reserves "!", RFC5987 does not,
+ // so we do not need to escape it
+ .replace(/['()]/g, escape) // i.e., %27 %28 %29
+ .replace(/\*/g, '%2A')
+ // The following are not required for percent-encoding per RFC5987,
+ // so we can allow for a little better readability over the wire: |`^
+ .replace(/%(?:7C|60|5E)/g, unescape);
+}
+
+module.exports = FileServer
diff --git a/src/app/global.js b/src/app/global.js
index 2ea77f5782..46682ccf62 100644
--- a/src/app/global.js
+++ b/src/app/global.js
@@ -2,7 +2,6 @@
var _ = require('underscore'),
async = require('async'),
inherits = require('util').inherits,
- Q = require('q'),
// Machine readable
os = require('os'),
dayjs = require('dayjs'),
diff --git a/src/app/httpapi.js b/src/app/httpapi.js
index fb239a81f8..fdfeb5ebce 100644
--- a/src/app/httpapi.js
+++ b/src/app/httpapi.js
@@ -13,7 +13,7 @@
}
var initServer = function () {
- return Q.Promise(function (resolve, reject) {
+ return new Promise(function (resolve, reject) {
server = rpc.Server({
'headers': { // allow custom headers is empty by default
'Access-Control-Allow-Origin': '*'
@@ -36,14 +36,12 @@
//Do a small delay before sending data in case there are more simultaneous events
var reinitTimeout = function () {
- win.debug('HttpAPI: reinitTimeout');
//Only do a delay if the request won't time out in the meantime
if (startTime + 8000 - (new Date()).getTime() > 250) {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(emitEvents, 200);
- win.debug('HttpAPI: setTimeout');
}
};
diff --git a/src/app/images/events/aprilsfool.png b/src/app/images/icons/events/aprilsfool.png
similarity index 100%
rename from src/app/images/events/aprilsfool.png
rename to src/app/images/icons/events/aprilsfool.png
diff --git a/src/app/images/events/halloween.png b/src/app/images/icons/events/halloween.png
similarity index 100%
rename from src/app/images/events/halloween.png
rename to src/app/images/icons/events/halloween.png
diff --git a/src/app/images/events/newyear.png b/src/app/images/icons/events/newyear.png
similarity index 100%
rename from src/app/images/events/newyear.png
rename to src/app/images/icons/events/newyear.png
diff --git a/src/app/images/events/pt_anniv.png b/src/app/images/icons/events/pt_anniv.png
similarity index 100%
rename from src/app/images/events/pt_anniv.png
rename to src/app/images/icons/events/pt_anniv.png
diff --git a/src/app/images/events/stpatrick.png b/src/app/images/icons/events/stpatrick.png
similarity index 100%
rename from src/app/images/events/stpatrick.png
rename to src/app/images/icons/events/stpatrick.png
diff --git a/src/app/images/events/stvalentine.png b/src/app/images/icons/events/stvalentine.png
similarity index 100%
rename from src/app/images/events/stvalentine.png
rename to src/app/images/icons/events/stvalentine.png
diff --git a/src/app/images/events/xmas.png b/src/app/images/icons/events/xmas.png
similarity index 100%
rename from src/app/images/events/xmas.png
rename to src/app/images/icons/events/xmas.png
diff --git a/src/app/images/icons/extratorrent-dark.png b/src/app/images/icons/extratorrent-dark.png
deleted file mode 100644
index 29ebd0cc86..0000000000
Binary files a/src/app/images/icons/extratorrent-dark.png and /dev/null differ
diff --git a/src/app/images/icons/extratorrent-light.png b/src/app/images/icons/extratorrent-light.png
deleted file mode 100644
index db10ee8cf9..0000000000
Binary files a/src/app/images/icons/extratorrent-light.png and /dev/null differ
diff --git a/src/app/images/icons/extratorrent.png b/src/app/images/icons/extratorrent.png
deleted file mode 100644
index db10ee8cf9..0000000000
Binary files a/src/app/images/icons/extratorrent.png and /dev/null differ
diff --git a/src/app/images/flags/none.svg b/src/app/images/icons/flag-none.svg
similarity index 100%
rename from src/app/images/flags/none.svg
rename to src/app/images/icons/flag-none.svg
diff --git a/src/app/images/flags/question.svg b/src/app/images/icons/flag-question.svg
similarity index 100%
rename from src/app/images/flags/question.svg
rename to src/app/images/icons/flag-question.svg
diff --git a/src/app/images/icons/getstrike.png b/src/app/images/icons/getstrike.png
deleted file mode 100644
index 95d94c9a96..0000000000
Binary files a/src/app/images/icons/getstrike.png and /dev/null differ
diff --git a/src/app/images/icons/icon-discourse.png b/src/app/images/icons/icon-discourse.png
index d844688fc0..aa5d0d162c 100644
Binary files a/src/app/images/icons/icon-discourse.png and b/src/app/images/icons/icon-discourse.png differ
diff --git a/src/app/images/icons/icon-facebook.png b/src/app/images/icons/icon-facebook.png
deleted file mode 100644
index 929b4b17ae..0000000000
Binary files a/src/app/images/icons/icon-facebook.png and /dev/null differ
diff --git a/src/app/images/icons/icon-gitlab.png b/src/app/images/icons/icon-gitlab.png
deleted file mode 100644
index a1986165d9..0000000000
Binary files a/src/app/images/icons/icon-gitlab.png and /dev/null differ
diff --git a/src/app/images/icons/icon-google.png b/src/app/images/icons/icon-google.png
deleted file mode 100644
index 01a729d2ca..0000000000
Binary files a/src/app/images/icons/icon-google.png and /dev/null differ
diff --git a/src/app/images/icons/icon-stash.png b/src/app/images/icons/icon-stash.png
deleted file mode 100644
index 5109bbca1f..0000000000
Binary files a/src/app/images/icons/icon-stash.png and /dev/null differ
diff --git a/src/app/images/icons/icon-twitter.png b/src/app/images/icons/icon-twitter.png
deleted file mode 100644
index e3ca075957..0000000000
Binary files a/src/app/images/icons/icon-twitter.png and /dev/null differ
diff --git a/src/app/images/icons/kickasstorrents.png b/src/app/images/icons/kickasstorrents.png
deleted file mode 100644
index a55adb187e..0000000000
Binary files a/src/app/images/icons/kickasstorrents.png and /dev/null differ
diff --git a/src/app/images/icons/Player/Sound0.png b/src/app/images/icons/player/Sound0.png
similarity index 100%
rename from src/app/images/icons/Player/Sound0.png
rename to src/app/images/icons/player/Sound0.png
diff --git a/src/app/images/icons/Player/Sound1.png b/src/app/images/icons/player/Sound1.png
similarity index 100%
rename from src/app/images/icons/Player/Sound1.png
rename to src/app/images/icons/player/Sound1.png
diff --git a/src/app/images/icons/Player/Sound2.png b/src/app/images/icons/player/Sound2.png
similarity index 100%
rename from src/app/images/icons/Player/Sound2.png
rename to src/app/images/icons/player/Sound2.png
diff --git a/src/app/images/icons/Player/Sound3.png b/src/app/images/icons/player/Sound3.png
similarity index 100%
rename from src/app/images/icons/Player/Sound3.png
rename to src/app/images/icons/player/Sound3.png
diff --git a/src/app/images/icons/Player/Subtitles.png b/src/app/images/icons/player/Subtitles.png
similarity index 100%
rename from src/app/images/icons/Player/Subtitles.png
rename to src/app/images/icons/player/Subtitles.png
diff --git a/src/app/images/icons/tpb-dark.png b/src/app/images/icons/tpb-dark.png
deleted file mode 100644
index 30f2cfb6c5..0000000000
Binary files a/src/app/images/icons/tpb-dark.png and /dev/null differ
diff --git a/src/app/images/icons/tpb-light.png b/src/app/images/icons/tpb-light.png
deleted file mode 100644
index 4e03e45504..0000000000
Binary files a/src/app/images/icons/tpb-light.png and /dev/null differ
diff --git a/src/app/index.html b/src/app/index.html
index 8e8aa98713..02df810acd 100644
--- a/src/app/index.html
+++ b/src/app/index.html
@@ -7,19 +7,16 @@
-
-
-
-
-
-
-
+
+
+
+
@@ -35,8 +32,7 @@
-
-
+
@@ -44,6 +40,7 @@
+
@@ -60,9 +57,9 @@
+
-
@@ -70,23 +67,15 @@
-
+
-
-
+
+
-
-
-
-
-
-
-
-
@@ -109,39 +98,33 @@
-
-
+
-
+
+
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-