├── .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: [](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 |
--------------------------------------------------------------------------------