├── .travis.yml ├── LICENSE ├── README.md ├── bash ├── download_magnets_in_rss.sh ├── download_tarball.sh ├── folder_structure.sh ├── install_additional_packages.sh ├── install_crontab.sh ├── install_flexget.sh ├── install_motd.sh ├── install_transmission.sh ├── settings.cfg ├── spotify.sh └── user_profile.sh ├── flexget └── config.yml ├── install.sh ├── logs └── .gitkeep ├── python └── delete_seen_shows.py └── test.sh /.travis.yml: -------------------------------------------------------------------------------- 1 | language: shell 2 | sudo: false 3 | script: 4 | - bash -c 'shopt -s globstar; ./test.sh' 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### ⚠️⚠️ Last tested version of OSMC is [**2020.03-1**](https://osmc.tv/download/images/). Newer versions probably need tweaking 2 | 3 | Build status: [![Build Status](https://travis-ci.org/alombarte/raspberry-osmc-automated.svg?branch=master)](https://travis-ci.org/alombarte/raspberry-osmc-automated) 4 | 5 | Setup of a **media center** with **automated episode downloads** in the background. 6 | 7 | These are the applications this setup configures and automates for you: 8 | 9 | - [OSMC](https://osmc.tv/) the operating system for the mediacenter, interacting with your own TV remote. 10 | - [Transmission](http://www.transmissionbt.com/) to download torrents 11 | - [Flex](http://flexget.com/) to automate jobs and such as the episode search, download, add subtitles or keep the house clean 12 | - [Subliminal](https://github.com/Diaoul/subliminal) to integrate subtitles in your language 13 | - Cronjobs, several of them to keep everything working in background 14 | 15 | Watch TV as soon shows are released without doing anything. 16 | 17 | 18 | ## Requirements 19 | - A running OSMC ([Download and install](https://osmc.tv/download/) here, easy as hell to install). 20 | - Hardware: A Raspberry Pi (1 or 2 or 3), Vero or Apple TV 21 | - An account in [ShowRSS](https://showrss.info/) or similar feed service that configures your own feed. 22 | 23 | This setup has been running since 2015 and now in 2018 has been updated to support raspberry 3 using OSMC release [**2018.03-2**](https://osmc.tv/download/images/) (if there is a newer version now, should work) 24 | 25 | ## Features 26 | After the installation you get a mediacenter operated from your TV remote. All your selected TV Shows are puctually downloaded in their correct folder with its subtitles, and the filesystem is kept clean and organized, no maintenance to do. Some of the features are: 27 | 28 | - Operation from the remote, no mouse nor keyboard needed. 29 | - The setup of your preferred TV Shows taken from ShowRSS or any other feed of your interest. 30 | - All downloads automated, moved to the right folder structure when completed. Using real episode names and season information (taken from TheTVDB) is optional by uncommenting lines in the config. 31 | - Automatic download of subtitles and retry of failed ones every hour. 32 | - Downloads folder and transmission tasks always clean. 33 | - Updated Kodi library with TV Shows covers and art cover. 34 | - [optional] Delete shows that have been already seen to save storage 35 | 36 | The following services can be used remotely: 37 | 38 | Service | Access | User / Password 39 | -------- | ---- | ----------- 40 | Transmission web client | http://$OSMC_HOST:9091 | transmission / transmission 41 | Kodi Web (Remote) | http://$OSMC_HOST/ | None 42 | Open SSH | ssh osmc@$OSMC_HOST | osmc/osmc (`sudo` is available) 43 | 44 | **Samba** is not installed, all my downloads go to an external storage (USB) that I can carry everywhere. Think that the Raspberry is not a regular PC and you can hit memory limits easily, the less services you put the better. Occasional transfers can be easily done using `scp myfile osmc@$OSMC_HOST`. 45 | 46 | 47 | # Installation 48 | In order to start the installation just SSH to your fresh OSMC installation in the raspeberry: 49 | 50 | ssh osmc@YOUR_OSMC_IP 51 | 52 | Then the installation starts by copy-pasting these lines: 53 | 54 | bash <(curl -H 'Pragma: no-cache' -H 'Cache-Control: no-cache' -s 'https://raw.githubusercontent.com/alombarte/raspberry-osmc-automated/master/install.sh') /home/osmc/.raspberry-osmc-automated 2>&1 | tee installation.log 55 | 56 | The installation script will download all necessary files and will configure the raspberry for you. At the beginning of the installation process you will be asked for your `RSS feed` URL, your IP and the mountpoint of your external storage (if any). Once you have answered the questions, leave the computer and do something else. The process is long, and if there is anything broken you will be able to see it in the `installation.log`. 57 | 58 | **Your mediacenter is ready!** 59 | 60 | ### Getting the RSS feed 61 | If you don't have a RSS you can sign up in a free service like [ShowRSS](http://showrss.info). Once you have done it add some TV Shows to your feed and then get the link by clicking ["Generate" in the feeds section](https://showrss.info/?cs=feeds) 62 | 63 | 64 | ## Extending the configuration 65 | If you want to tweak on the existing installation, this might help you a little bit. To begin with, all the configuration variables you chose during installation are saved in `/home/osmc/.raspberry-osmc-automated/bash/settings.cfg`. Don't change them because it doesn't have any effect, they are used only during the installation and are there for informative purposes afterwards. 66 | 67 | The following paths assume you installed the application under `/home/osmc/.raspberry-osmc-automated` 68 | 69 | ### Changing the automation of TV Shows 70 | The Real magic happens by configuring this task. The default task downloads all episodes to `/home/osmc/TV Shows`, it uses a structure like `TV Shows/Show name/Season 1/Episode Name` 71 | 72 | For any changes edit the file: 73 | 74 | /home/osmc/.raspberry-osmc-automated/flexget/config.yml 75 | 76 | Then execute flexget to see if it's working (the flexget daemon might need to be restarted): 77 | 78 | /usr/local/bin/flexget exec 79 | 80 | #### Subtitles 81 | All jobs executed by flexget related with `TV Shows` try to download the subtitle in the language you chose during installation. There are 2 different attempts 2 download subtitles: 82 | 83 | - Just after an episode is downloaded: Flexget will use the `subliminal` tool to get the associate subtitle. This operation might fail if you are downloading a just aired episode and the subtitles have not been written yet. 84 | - In order to complete any missing subtitles, every hour a cron job tries to download missing ones. 85 | 86 | If post-install you ever need to change the subtitle language you need to edit 2 files: 87 | 88 | - ` /home/osmc/.raspberry-osmc-automated/flexget/config.yml` and change the 2 letter code (e.g `es`) in the following line: `exec: subliminal download -l es ....` 89 | - In the crontab with the command `crontab -e` and look for the line invoking `subliminal` 90 | 91 | ##### Using your addic7ed account 92 | If you have an account in `addic7ed` you can pass your credentials in the aforementioned places adding the flag `--addic7ed YOURUSER YOURPASSWORD`. E.g: 93 | 94 | /usr/local/bin/subliminal --addic7ed YOURUSER YOURPASSWORD download -l es -s "/home/osmc/TV Shows/" 95 | 96 | ### Crontab 97 | If you want to change the frequency of the feed checking, add, or remove jobs just execute: 98 | 99 | crontab -e 100 | 101 | #### Deleting seen shows 102 | In the crontab there is a commented job that deletes TV shows that are marked as seen by Kodi after 1 month. If you want to enable this feature just uncomment the line in the crontab and save. 103 | 104 | ### SSH without password: 105 | If you want to stop typing the password every time you SSH to the Raspberry Pi do the following in your Mac/Linux: 106 | 107 | # Put here your raspberry IP, e.g: 108 | OSMC_HOST=192.168.1.10 109 | 110 | # Copy SSH key to OSMC server 111 | cat ~/.ssh/id_rsa.pub | ssh osmc@$OSMC_HOST 'mkdir -p ~/.ssh; umask 077; cat >>~/.ssh/authorized_keys' 112 | 113 | 114 | ### Broken locales? 115 | If you see this message on every login: 116 | 117 | -bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)` 118 | 119 | This is a possible fix: 120 | 121 | sudo locale-gen "en_US.UTF-8" 122 | sudo dpkg-reconfigure locales 123 | # Choose en_US.UTF-8 124 | 125 | ### Other utils 126 | #### Download all magnets in RSS feed. 127 | The system keeps track of your series feed and downloads periodically all files found but you might need from time to time to parse and extract from another one (e.g: first day you download a new serie). 128 | There is a bash script that adds in your transmission all magnets found: 129 | 130 | bash /home/osmc/.raspberry-osmc-automated/bash/download_magnets_in_rss.sh http://showrss.info/show/117.rss 131 | 132 | The previous call would add to Transmission all magnets available for the serie. 133 | 134 | ## Spotify and Youtube 135 | The spotify plugin doesn't come preinstalled. If you have a Premium account you will be able to install it using `bash/spotify.sh`. 136 | 137 | The Youtube plugin can be installed manually from the Add-on repository browser. 138 | -------------------------------------------------------------------------------- /bash/download_magnets_in_rss.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script adds to transmission anything inside a tag starting with "magnet" in the provided URL. 3 | 4 | if [ $# != 1 ] 5 | then 6 | echo "Adds all magnets inside a ShowRSS URL to transmission" 7 | echo "--USAGE: $0 \"http://showrss.info/show/117.rss\"" 8 | exit 0 9 | fi 10 | 11 | for magnet in $(wget -q -O- "$1" | grep -oPm1 "(?<=)[^<]+" | grep magnet | sort | uniq) 12 | do 13 | echo "Adding magnet $magnet" 14 | transmission-remote -a "$magnet" 15 | done 16 | 17 | 18 | -------------------------------------------------------------------------------- /bash/download_tarball.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# != 1 ] 4 | then 5 | echo "Installation of raspberry-osmc-automated" 6 | echo "--USAGE: $0 install_path" 7 | exit 0 8 | fi 9 | 10 | INSTALLATION_FOLDER=$1 11 | SETTINGS_FILE="$INSTALLATION_FOLDER/bash/settings.cfg" 12 | SOURCE="https://github.com/alombarte/raspberry-osmc-automated/archive/master.zip" 13 | 14 | echo "Downloading source from $SOURCE..." 15 | curl -L -O $SOURCE && \ 16 | unzip master.zip && \ 17 | mv raspberry-osmc-automated-master "$INSTALLATION_FOLDER" && \ 18 | rm master.zip 19 | 20 | if [ ! -f "$SETTINGS_FILE" ]; then 21 | echo "Download failed. File $SETTINGS_FILE does not exist" 22 | echo "Aborting..." 23 | exit 0 24 | fi 25 | 26 | echo "Source downloaded sucessfully to $INSTALLATION_FOLDER" -------------------------------------------------------------------------------- /bash/folder_structure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# != 1 ] 4 | then 5 | echo "Creates the folder structure in the external device." 6 | echo "--USAGE: $0 path" 7 | echo "-- e.g: $0 /media/USB" 8 | exit 0 9 | fi 10 | 11 | EXTERNAL_STORAGE=$1 12 | mkdir -p "$EXTERNAL_STORAGE"/{Downloads/Incomplete,Movies,Music,Pictures,"TV Shows"} 13 | 14 | echo "Deleting default OSMC folders." 15 | # Delete default OSMC media folders in ~home and put symlinks to the USB. 16 | sudo rmdir /home/osmc/{Movies,Music,Pictures,"TV Shows"} 17 | echo "Recreating folders but pointing to the external storage" 18 | ln -s --force "$EXTERNAL_STORAGE"/{Downloads,Movies,Music,Pictures,"TV Shows"} ~/ 19 | 20 | # The home directory `/home/omsc` now looks like this: 21 | # 22 | # lrwxrwxrwx 1 osmc osmc 25 Sep 4 18:54 Downloads -> /media/KINGSTON/Downloads 23 | # lrwxrwxrwx 1 osmc osmc 22 Sep 4 18:54 Movies -> /media/KINGSTON/Movies 24 | # lrwxrwxrwx 1 osmc osmc 21 Sep 4 18:54 Music -> /media/KINGSTON/Music 25 | # lrwxrwxrwx 1 osmc osmc 24 Sep 4 18:54 Pictures -> /media/KINGSTON/Pictures 26 | # lrwxrwxrwx 1 osmc osmc 24 Sep 4 18:54 TV Shows -> /media/KINGSTON/TV Shows 27 | 28 | -------------------------------------------------------------------------------- /bash/install_additional_packages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Packages not needed by the system, but useful 4 | PACKAGES=(git vim sendmail) 5 | 6 | echo "The following packages are NOT needed by the mediacenter." 7 | echo "Press 'y' to install if you'll need the package or any other key to discard it:" 8 | for pkg in "${PACKAGES[@]}" 9 | do 10 | read -p "- Install $pkg? [y/N] " -n 1 -r 11 | if [[ $REPLY =~ ^[Yy]$ ]] 12 | then 13 | sudo apt-get install "$pkg" --yes 14 | fi 15 | done 16 | -------------------------------------------------------------------------------- /bash/install_crontab.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# != 1 ] 3 | then 4 | echo "Installs the crontab" 5 | echo "--USAGE: $0 2_letter_code_subtitles_language" 6 | echo "e.g: $0 es (Cron will download subtitles in Spanish)" 7 | exit 0 8 | fi 9 | # Install cron 10 | sudo apt-get install cron --yes 11 | 12 | # Crontab content 13 | cat < extra_lines 14 | @reboot /usr/local/bin/flexget daemon start -d > /dev/null 15 | @hourly /usr/local/bin/flexget --cron execute > /dev/null 16 | @hourly wget --quiet --header='Content-Type: application/json' --post-data='{"method": "VideoLibrary.Scan", "id":5,"jsonrpc":"2.0"}' http://localhost/jsonrpc -O /dev/null 17 | @daily wget --quiet --header='Content-Type: application/json' --post-data='{"method": "VideoLibrary.Clean", "id":5,"jsonrpc":"2.0"}' http://localhost/jsonrpc -O /dev/null 18 | # Delete images, text files and existing subtitles. Then delete any empty dir in Downloads. 19 | @hourly find /home/osmc/Downloads/. \( -name "*.jpg" -o -name "*.exe" -o -name "*.png" -o -name "*.txt" -o -name "*.url" -o -name "*.nfo" -o -name "*.srt" \) -delete && find /home/osmc/Downloads/. -mindepth 1 -type d -empty -delete 20 | @hourly /usr/local/bin/subliminal download -l SUBTITLES_LANGUAGE -s /home/osmc/TV\ Shows/* --age 1w > /dev/null 21 | # Uncomment to delete shows already seen 22 | # @weekly python /home/osmc/.raspberry-osmc-automated/python/delete_seen_shows.py --delete > /dev/null 23 | EOF 24 | 25 | echo "Adding lines to user crontab:" 26 | sed -i "s/SUBTITLES_LANGUAGE/$1/" extra_lines 27 | 28 | echo "Forcing UTF-8 in crontab" 29 | cat <<'EOF' | tee ~/.bashrc 30 | export LC_ALL="en_US.UTF-8" 31 | export LANG="en_US.UTF-8" 32 | export LANGUAGE="en_US.UTF-8" 33 | EOF 34 | sudo service cron restart 35 | 36 | # Save current crontab content 37 | crontab -l > crontab_content 38 | # Append extra lines 39 | cat extra_lines >> crontab_content 40 | crontab crontab_content 41 | 42 | # Cleaning 43 | rm crontab_content extra_lines 44 | 45 | echo "Your crontab is now as follows:" 46 | crontab -l 47 | 48 | -------------------------------------------------------------------------------- /bash/install_flexget.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# != 3 ] 3 | then 4 | echo "Installs and configures flexget in the system." 5 | echo "--USAGE: $0 install_path subtitles_language feed" 6 | echo "e.g: $0 /home/osmc/.raspberry-osmc-automated es 'http://showrss.info/rss.php?user_id=51436'" 7 | exit 0 8 | fi 9 | 10 | INSTALLATION_FOLDER=$1 11 | SUBTITLES_LANGUAGE=$2 12 | CONFIG_RSS_FEED=$(echo "$3" | sed -e 's/[\/&]/\\&/g') 13 | 14 | # Dependencies: 15 | sudo apt-get install python-pip python-setuptools --yes 16 | # six>=1.9.0 to run flex needed: 17 | sudo pip install six --upgrade 18 | # Only flexget 2.x.x is supported by Python 2.7 19 | sudo pip install --upgrade setuptools 20 | sudo pip install "flexget>=2.0,<3.0" 21 | 22 | # Add FlexGet configuration: 23 | mkdir -p ~/.flexget 24 | if [ -f /home/osmc/.flexget/config.yml ]; 25 | then 26 | mv /home/osmc/.flexget/config.yml /home/osmc/.flexget/config.yml.default 27 | fi 28 | ln -s "$INSTALLATION_FOLDER/flexget/config.yml" /home/osmc/.flexget/config.yml 29 | 30 | echo "Flexget will download content from RSS: $CONFIG_RSS_FEED" 31 | 32 | # Write user's feed in the .yml 33 | sed -i "s@rss: CONFIG_RSS_FEED@rss: $CONFIG_RSS_FEED@" "$INSTALLATION_FOLDER/flexget/config.yml" 34 | sed -i "s@SUBTITLES_LANGUAGE@$SUBTITLES_LANGUAGE@" "$INSTALLATION_FOLDER/flexget/config.yml" 35 | 36 | # Works? 37 | /usr/local/bin/flexget exec 38 | 39 | # Subtitles: 40 | sudo pip install subliminal -------------------------------------------------------------------------------- /bash/install_motd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # font: http://patorjk.com/software/taag/#p=display&f=Slant&t=Mediacenter 4 | cat <<'EOF' | sudo tee /etc/motd 5 | __ ___ ___ __ 6 | / |/ /__ ____/ (_)___ _________ ____ / /____ _____ 7 | / /|_/ / _ \/ __ / / __ `/ ___/ _ \/ __ \/ __/ _ \/ ___/ 8 | / / / / __/ /_/ / / /_/ / /__/ __/ / / / /_/ __/ / 9 | /_/ /_/\___/\__,_/_/\__,_/\___/\___/_/ /_/\__/\___/_/ 10 | Info: https://github.com/alombarte/raspberry-osmc-automated 11 | 12 | EOF 13 | -------------------------------------------------------------------------------- /bash/install_transmission.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# != 1 ] 4 | then 5 | echo "Installs transmission and gives permissions to access from a network range" 6 | echo "--USAGE: $0 'network_range'" 7 | echo "-- e.g: $0 '192.168.*.*'" 8 | exit 0 9 | fi 10 | 11 | NETWORK_RANGE=$1 12 | 13 | sudo apt-get install transmission-daemon python-transmissionrpc transmission-cli --yes 14 | 15 | # Start the daemon (let config files be created) 16 | sudo /etc/init.d/transmission-daemon start 17 | 18 | # Stop daemon to edit settings, otherwise they are rewritten: 19 | sudo /etc/init.d/transmission-daemon stop 20 | 21 | # Change downloading dirs from/var/lib/transmission-daemon/ to home folder: 22 | sudo sed -i 's@/var/lib/transmission-daemon/downloads@/home/osmc/Downloads@' /etc/transmission-daemon/settings.json 23 | sudo sed -i 's@/var/lib/transmission-daemon/Downloads@/home/osmc/Downloads/Incomplete@' /etc/transmission-daemon/settings.json 24 | echo "Transmission access is allowed to range: $NETWORK_RANGE" 25 | sudo sed -i "s@\"rpc-whitelist\": \"127.0.0.1\"@\"rpc-whitelist\": \"127.0.0.1,$NETWORK_RANGE\"@" /etc/transmission-daemon/settings.json 26 | sudo sed -i "s@\"rpc-whitelist\": \"127.0.0.1\"@\"rpc-whitelist\": \"127.0.0.1,$NETWORK_RANGE\"@" /home/osmc/.config/transmission-daemon/settings.json 27 | 28 | # Previous access doesn't seem to be enough in some reported cases. 29 | # Enable access to the GUI from inside the network: 30 | cat < Addons > Install from ZIP" 7 | echo "Choose the ZIP file in the Home folder and follow the instructions. You'll need a Spotify premium account" 8 | 9 | -------------------------------------------------------------------------------- /bash/user_profile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #### I like prompt with colors: 4 | sed -i 's/#force_color_prompt=yes/force_color_prompt=yes/' ~/.bashrc 5 | 6 | # Default editor 7 | echo "export EDITOR=vi" >> ~/.bash_aliases 8 | 9 | -------------------------------------------------------------------------------- /flexget/config.yml: -------------------------------------------------------------------------------- 1 | # email: 2 | # from: someuser@gmx.com 3 | # to: 4 | # - someuser@gmx.com 5 | # smtp_host: smtp.gmx.com 6 | # smtp_port: 25 7 | # smtp_username: someuser@gmx 8 | # smtp_password: XXXXXXX 9 | # template: html 10 | 11 | templates: 12 | # This template is used to sort the problematic series 13 | # http://flexget.com/Cookbook/Series/Sort 14 | # You must use series groups, so that we can turn off filtering with the parse_only option in our sort feed. 15 | tv-series: 16 | series: 17 | myseriesgroup: 18 | - Grey's Anatomy: 19 | set: 20 | tvdb_id: 73762 21 | - Designated Survivor 22 | 23 | tasks: 24 | # downloading task 25 | download-rss: 26 | # RSS where everything is downloaded from: 27 | rss: CONFIG_RSS_FEED 28 | # fetch all the feed series 29 | all_series: yes 30 | # use transmission to download the torrents 31 | transmission: 32 | host: localhost 33 | port: 9091 34 | username: transmission 35 | password: transmission 36 | path: /home/osmc/Downloads 37 | # Remove completed downloads from Transmission 38 | clean_transmission: 39 | host: localhost 40 | port: 9091 41 | username: transmission 42 | password: transmission 43 | finished_for: 1 hours 44 | # sorting task 45 | sort-files: 46 | filesystem: 47 | # directory with the files to be sorted 48 | path: /home/osmc/Downloads/ 49 | # fetch all avi, mkv and mp4 files, skips the .part files (unfinished torrents) 50 | regexp: '.*\.(avi|mkv|mp4)$' 51 | recursive: yes 52 | accept_all: yes 53 | disable: 54 | - seen 55 | - retry_failed 56 | # this is needed for the episode names 57 | thetvdb_lookup: yes 58 | template: tv-series 59 | all_series: 60 | # for some reason all_series rejects all episodes here, even with seen: local, so parse_only must be added 61 | parse_only: yes 62 | # NOTE: You must set the parse_only option for all of the series groups you have configured in your template. 63 | # This option prevents the series plugin from accepting or rejecting anything in this feed. 64 | series: 65 | settings: 66 | myseriesgroup: 67 | parse_only: yes 68 | # Ignore samples 69 | regexp: 70 | reject: 71 | - sample 72 | # With the require_field and accept_all plugins, we accept anything that the series plugin has successfully parsed. 73 | require_field: series_name 74 | move: 75 | # this is where the series will be put. 76 | to: /home/osmc/TV Shows/{{ tvdb_series_name }} 77 | # If you want to rename the filename to something like "Series Name - S01E02 - Episode Name.mkv" uncomment the line below and 78 | # swap the "exec" below as well. It is commented by default as the service providing the metadata cannot always match. 79 | #filename: "{{ tvdb_series_name }} - {{ series_id|default('XX') }} {% if tvdb_ep_name|default(False) %} - {{ tvdb_ep_name }}{% endif %}{{ location|pathext }}" 80 | # Download all missing subtitles for this TV Show in your chosen language (subliminal will ignore series with subtitles already): 81 | exec: /usr/local/bin/subliminal download -l "SUBTITLES_LANGUAGE" -s "/home/osmc/TV Shows/{{ tvdb_series_name }}/" 82 | # Use this line instead if you choose to rename files: 83 | #exec: /usr/local/bin/subliminal download -l "SUBTITLES_LANGUAGE" -s "/home/osmc/TV Shows/{{ tvdb_series_name }}/{{ tvdb_series_name }} - {{ series_id|default('XX') }} {% if tvdb_ep_name|default(False) %} - {{ tvdb_ep_name }}{% endif %}{{ location|pathext }}" 84 | 85 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | # You can execute this script with: 5 | # bash <(curl -H 'Pragma: no-cache' -H 'Cache-Control: no-cache' -s 'https://raw.githubusercontent.com/alombarte/raspberry-osmc-automated/master/install.sh') /home/osmc/.raspberry-osmc-automated 6 | 7 | if [ $# != 1 ] 8 | then 9 | echo "Installation of raspberry-osmc-automated" 10 | echo "--USAGE: $0 install_path" 11 | exit 0 12 | fi 13 | 14 | if [ "root" = "$(whoami)" ]; then 15 | echo "Do not run this script as root. The sudo command will be used when needed." 16 | exit 0 17 | fi 18 | 19 | INSTALLATION_FOLDER=$1 20 | SETTINGS_FILE="$INSTALLATION_FOLDER/bash/settings.cfg" 21 | SAMPLE_RSS_FEED="http://showrss.info/rss.php?user_id=51436&hd=2&proper=1" 22 | HOME_FOLDER="/home/osmc" 23 | 24 | 25 | echo "Starting installation in path $INSTALLATION_FOLDER" 26 | echo "Press ENTER to proceed or CTRL+C to abort" 27 | echo "--------------------------------------------------" 28 | read -r _ 29 | 30 | curl -L -H 'Pragma: no-cache' -H 'Cache-Control: no-cache' -O 'https://raw.githubusercontent.com/alombarte/raspberry-osmc-automated/master/install.sh' 31 | 32 | # DOWNLOAD TARBALL 33 | bash <(curl -s https://raw.githubusercontent.com/alombarte/raspberry-osmc-automated/master/bash/download_tarball.sh) "$INSTALLATION_FOLDER" 34 | 35 | # Add installation path to settings 36 | echo "INSTALLATION_FOLDER=$INSTALLATION_FOLDER" >> "$SETTINGS_FILE" 37 | 38 | # Ask user for default paths 39 | echo "--------------------------------------------------" 40 | echo "Take a few seconds to customize your installation." 41 | echo "" 42 | echo "Paste your RSS feed URL below or press ENTER to use sample" 43 | echo "E.g: : $SAMPLE_RSS_FEED" 44 | read -r CONFIG_RSS_FEED 45 | if [ "$CONFIG_RSS_FEED" == "" ] ; then 46 | CONFIG_RSS_FEED=$SAMPLE_RSS_FEED 47 | echo "No RSS provided, using sample feed $CONFIG_RSS_FEED" 48 | fi 49 | # Add RSS feed to settings 50 | echo "CONFIG_RSS_FEED=\"$CONFIG_RSS_FEED\"" >> "$SETTINGS_FILE" 51 | 52 | # Confirm IP 53 | IP_GUESS=$(ifconfig | awk '/inet /{print $2}' | grep -v 127.0.0.1 | tail -n 1) 54 | echo "" 55 | echo "IP address (or ENTER to accept) [$IP_GUESS]: " 56 | read -r IP 57 | if [ "$IP" == "" ] ; then 58 | IP=$IP_GUESS 59 | fi 60 | IP_RANGE=$(echo "$IP" | sed -r 's/([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)[0-9]{1,3}/\1*/') 61 | echo "OSMC_HOST=\"$IP\"" >> "$SETTINGS_FILE" 62 | echo "TRANSMISSION_WHITELIST_RANGE=\"$IP_RANGE\"" >> "$SETTINGS_FILE" 63 | 64 | echo "" 65 | echo "Are you storing content on a external storage? If so, what is the mountpoint? (e.g: /media/KINGSTON)" 66 | echo "Press ENTER to skip, or write the path now:" 67 | read -r EXTERNAL_STORAGE 68 | if [ "$EXTERNAL_STORAGE" == "" ] || [ ! -d "$EXTERNAL_STORAGE" ] ; then 69 | echo "Omitting external storage. Preparing folder structure..." 70 | EXTERNAL_STORAGE=$HOME_FOLDER 71 | mkdir -p "$EXTERNAL_STORAGE"/{Downloads/Incomplete,Movies,Music,Pictures,"TV Shows"} 72 | 73 | else 74 | bash "$INSTALLATION_FOLDER/bash/folder_structure.sh" "$EXTERNAL_STORAGE" 75 | echo "EXTERNAL_STORAGE=\"$EXTERNAL_STORAGE\"" >> "$SETTINGS_FILE" 76 | fi 77 | 78 | echo "" 79 | echo "In what language do you want the subtitles? " 80 | echo "(Two letter code: en,es,fr,de,it...) : " 81 | read -r SUBTITLES_LANGUAGE 82 | if [ "$SUBTITLES_LANGUAGE" == "" ] ; then 83 | echo "Default to English subtitles" 84 | SUBTITLES_LANGUAGE="en" 85 | fi 86 | 87 | echo "--------------------------------------------------" 88 | # INSTALLATION BEGIN 89 | echo "All set. INSTALLING..." 90 | 91 | echo "Updating APT repositories" 92 | sudo apt-get update 93 | 94 | # Add write permission to osmc group. We will add other users in this group 95 | chmod -R 775 $HOME_FOLDER 96 | 97 | bash "$INSTALLATION_FOLDER/bash/install_transmission.sh" "$IP_RANGE" 98 | bash "$INSTALLATION_FOLDER/bash/install_flexget.sh" "$INSTALLATION_FOLDER" "$SUBTITLES_LANGUAGE" "$CONFIG_RSS_FEED" 99 | bash "$INSTALLATION_FOLDER/bash/install_crontab.sh" "$SUBTITLES_LANGUAGE" 100 | bash "$INSTALLATION_FOLDER/bash/user_profile.sh" 101 | bash "$INSTALLATION_FOLDER/bash/install_additional_packages.sh" 102 | bash "$INSTALLATION_FOLDER/bash/install_motd.sh" 103 | 104 | echo "--------------------------------------------------" 105 | echo " Installation complete!" 106 | echo "--------------------------------------------------" 107 | echo "The following services are also available from your network *:" 108 | echo "" 109 | echo "Transmission web client (montitor ongoing downloads)" 110 | echo "- http://$IP:9091 (credentials: transmission / transmission)" 111 | echo "" 112 | echo "Kodi Web (remote control)" 113 | echo "- http://$IP" 114 | echo "" 115 | echo "--------------------------------------------------" 116 | echo "" 117 | echo "You might want to set your Timezone now" 118 | echo "Run: sudo dpkg-reconfigure tzdata" 119 | echo "" 120 | echo "You might also want to move this file away" 121 | echo "Run: mv install.sh /home/osmc/.raspberry-osmc-automated/" 122 | -------------------------------------------------------------------------------- /logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alombarte/raspberry-osmc-automated/5ea1ad47290804eb676c7147254754a953563b3e/logs/.gitkeep -------------------------------------------------------------------------------- /python/delete_seen_shows.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | """delete_seen_shows.py: Delete shows marked as seen at least 15 days ago in Kodi.""" 5 | 6 | import json 7 | import httplib 8 | import os 9 | import argparse 10 | import logging 11 | 12 | 13 | def main(): 14 | logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', 15 | filename='/home/osmc/.raspberry-osmc-automated/logs/delete_seen_shows.log', level=logging.INFO) 16 | args = get_parsed_arguments() 17 | seen_episodes = get_seen_episodes_from_rpc(args.host) 18 | 19 | for episode in seen_episodes: 20 | if args.delete and args.host == 'localhost': 21 | if delete_file_and_subtitle(episode['file'].encode('utf-8')): 22 | logging.info("Deleted episode " + 23 | episode['title'].encode('utf-8')) 24 | else: 25 | print_episode(episode) 26 | 27 | show_count = len(seen_episodes) 28 | logging.info("TOTAL: " + str(show_count)) 29 | 30 | # After deleting all files the DB needs to refresh the changes. 31 | if args.delete: 32 | clean_library_database(args.host) 33 | 34 | 35 | def get_parsed_arguments(): 36 | """ 37 | Returns the parsed arguments given in the command line 38 | """ 39 | 40 | parser = argparse.ArgumentParser( 41 | description='Lists all TV Shows marked as seen and allows to delete them') 42 | 43 | # Optional arguments 44 | parser.add_argument('-d', '--delete', 45 | action='store_true', # Allows this argument to behave like a flag 46 | help='Delete files and subtitles of seen TV shows. Ignored when using the flag --host' 47 | ) 48 | parser.add_argument('-H', '--host', 49 | help="""Hostname or IP of a remote machine running the Kodi JSON-RPC service. 50 | Remote deletion of files through the RPC service is not suported. Ignores 51 | flag --delete if present.""", 52 | default="localhost" 53 | ) 54 | args = parser.parse_args() 55 | return args 56 | 57 | 58 | def print_episode(episode): 59 | logging.info("--") 60 | logging.info(episode['label'].encode('utf-8')) 61 | logging.info(episode['file'].encode('utf-8')) 62 | logging.info("Last played: " + episode['lastplayed']) 63 | logging.info("Play count: " + str(episode['playcount'])) 64 | 65 | 66 | def delete_file_and_subtitle(file_to_delete): 67 | try: 68 | logging.info(file_to_delete) 69 | os.remove(file_to_delete) 70 | is_deleted = True 71 | 72 | except OSError as e: 73 | logging.error(e) 74 | is_deleted = False 75 | 76 | try: 77 | # Delete subtitle if present: 78 | basename = os.path.splitext(file_to_delete)[0] 79 | subtitle = basename + ".srt" 80 | if os.path.isfile(subtitle): 81 | os.remove(subtitle) 82 | 83 | except OSError as e: 84 | logging.error(e) 85 | 86 | return is_deleted 87 | 88 | 89 | def get_seen_episodes_from_rpc(rpc_host='localhost'): 90 | """ 91 | Retrieves from Kodi JSON-RPC service the list of episodes 92 | marked as seen and not played in the last month (avoids 93 | immediate deletion) 94 | """ 95 | 96 | connection = httplib.HTTPConnection(rpc_host, 80) 97 | connection.connect() 98 | connection.request('POST', '/jsonrpc', json.dumps({ 99 | "jsonrpc": "2.0", 100 | "method": "VideoLibrary.GetEpisodes", 101 | "params": { 102 | "filter": { 103 | "and": [ 104 | { 105 | "field": "lastplayed", 106 | "operator": "notinthelast", 107 | "value": "15 days" 108 | }, 109 | { 110 | "field": "playcount", 111 | "operator": "greaterthan", 112 | "value": "0" 113 | } 114 | ] 115 | }, 116 | "properties": [ 117 | "title", 118 | "playcount", 119 | "lastplayed", 120 | "file" 121 | ], 122 | "sort": { 123 | "order": "ascending", 124 | "method": "label" 125 | } 126 | }, 127 | "id": "libTvShows" 128 | }), { 129 | "Content-Type": "application/json" 130 | }) 131 | 132 | response = connection.getresponse() 133 | if 200 == response.status: 134 | response_object = json.loads(response.read()) 135 | 136 | if 'episodes' in response_object['result']: 137 | return response_object['result']['episodes'] 138 | else: 139 | logging.error('No shows match the minimum criteria') 140 | else: 141 | logging.error('Impossible to retrieve episodes from %r' % rpc_host) 142 | 143 | return [] 144 | 145 | 146 | def clean_library_database(rpc_host='localhost'): 147 | connection = httplib.HTTPConnection(rpc_host, 80) 148 | connection.connect() 149 | connection.request('POST', '/jsonrpc', json.dumps({ 150 | "jsonrpc": "2.0", 151 | "method": "VideoLibrary.Clean", 152 | "id": "libTvShows" 153 | }), { 154 | "Content-Type": "application/json" 155 | }) 156 | 157 | response = connection.getresponse() 158 | if 200 != response.status: 159 | logging.error('Could not clean database on %r' % rpc_host) 160 | 161 | 162 | if __name__ == "__main__": 163 | main() 164 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | ( 5 | # find all bash files and run `shellcheck` 6 | find . -name '*.sh' -exec sh -c ' 7 | shellcheck "$1" && echo "[PASS] $1" 8 | ' sh {} \; 9 | ruby -e "require 'yaml'; YAML.load_file('flexget/config.yml');"; 10 | ) || true 11 | --------------------------------------------------------------------------------