Compare commits

32 Commits

Author SHA1 Message Date
c2c5bd1396 Changing pyfbx target 2025-01-11 18:24:15 +01:00
9c40b58301 Trying to fix wrong pyfbx 2025-01-11 18:15:21 +01:00
cd7495a4d9 Change pyfbx target 2025-01-11 18:14:00 +01:00
de9398ef84 Edit Readme to remove old instructions 2025-01-11 18:09:06 +01:00
7a98a0ac84 Switched from Télé 7 Jours to NouvelObs as Télé 7 Jours drastically changed its interface 2025-01-11 18:02:22 +01:00
298fa75a60 Add warning about broken features. 2025-01-11 12:25:00 +01:00
667a43263d Update Pipfile 2024-12-07 21:31:20 +01:00
5d5fc16609 Merge branch 'master' of ssh://djib.fr:2311/djib/FreeboxMoviePlanner 2024-11-24 10:22:45 +01:00
9c28e99676 Wait for keypress when finised 2024-11-24 10:19:02 +01:00
c7f37b164e Updated URL after domain migration 2024-10-20 12:32:46 +02:00
a0ed6908cb Fix links 2024-02-29 16:19:27 +01:00
9c4d01eba2 Fix an crash in conflict detection when no movie is selected 2023-02-04 10:34:17 +01:00
2f0ce4ded4 Clean file names to better detect duplicates 2023-01-21 11:51:42 +01:00
249a3c51de Fix bad translation in code 2021-06-12 10:15:52 +02:00
10368df6f9 Remove debug print statement 2020-12-12 14:37:57 +01:00
47c5c3380f Fix --exclude-directory argument 2020-12-05 16:53:26 +01:00
c9eb733014 Changed Freebox discover method to work with VPN 2020-07-04 14:23:44 +02:00
56b35e358e Fix excluded directory not working 2020-03-14 16:05:19 +01:00
0ff1f73039 Fix excluded directory not working 2020-03-14 16:04:53 +01:00
0214112d7b Add the option to excluded movies available on local drive 2020-01-06 12:38:57 +01:00
4900c4649a Fix crash when TMDB API doesn't provide a release date 2019-12-28 08:26:24 +01:00
3476872925 Improve message when a movie is skipped 2019-12-21 08:59:32 +01:00
4cfef10989 Documentation for -e option 2019-12-14 12:01:27 +01:00
354f392c26 Add an option to manually filter channels 2019-12-14 09:10:20 +01:00
68b5f05928 Fix error when there is no release date 2019-10-26 15:03:41 +02:00
d782762e32 Fix typo 2019-10-23 21:07:57 +02:00
857ce38b57 Changed verbose mode to log level INFO 2019-10-19 09:22:01 +02:00
7384c85567 Add movie release Year 2019-10-19 09:20:17 +02:00
18458d059f Add command line argument to increase log verbosity 2019-10-19 09:09:58 +02:00
0564ba6f75 Fix typos 2019-10-16 11:02:51 +02:00
f8291526d2 Fix typos 2019-10-16 11:02:12 +02:00
8d7911f20c Update Readme since config.template.json is now in the release zip 2019-10-16 10:54:22 +02:00
6 changed files with 197 additions and 98 deletions

2
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "pyfbx"] [submodule "pyfbx"]
path = pyfbx path = pyfbx
url = https://djib.fr/djib/pyfbx.git url = https://framagit.org/djib/pyfbx.git

View File

@ -7,6 +7,7 @@ import argparse
import datetime import datetime
import json import json
import logging import logging
import os
import re import re
import requests import requests
import socket import socket
@ -33,15 +34,17 @@ class Movie:
self.date = 0 self.date = 0
self.start_time = 0 self.start_time = 0
self.end_time = 0 self.end_time = 0
self.year = ''
def __str__(self): def __str__(self):
return '{}: {} - {} ({})\n TMDB: {} - {}\n @ {}\n {}'.format( return '{}: {} - {} ({})\n TMDB: {} - {} - {}\n @ {}\n {}'.format(
'Today' if self.day == '' else self.day, 'Today' if self.day == '' else self.day,
self.title, self.title,
self.genre, self.genre,
self.channel, self.channel,
self.rating, self.rating,
self.original_title, self.original_title,
self.year,
self.url, self.url,
self.overview self.overview
) )
@ -56,15 +59,14 @@ class Movie:
class TVGuideScraper: class TVGuideScraper:
TV_GUIDE_URL = 'https://www.programme-television.org/{}?bouquet=free' TV_GUIDE_URL = 'https://programme-tv.nouvelobs.com/programme-free/categorie-film/{}/'
def findAllMovies(): def findAllMovies():
movies = [] movies = []
days = deque(['lundi', 'mardi', 'mercredi', days = deque(['lundi', 'mardi', 'mercredi',
'jeudi', 'vendredi', 'samedi', 'dimanche']) 'jeudi', 'vendredi', 'samedi', 'dimanche'])
offset = datetime.datetime.today().weekday() offset = datetime.datetime.today().weekday()
days.rotate(-1-offset) days.rotate(-offset)
days.appendleft('')
date = datetime.date.today() date = datetime.date.today()
for day in days: for day in days:
movies += TVGuideScraper._getMovies(day, date) movies += TVGuideScraper._getMovies(day, date)
@ -74,44 +76,45 @@ class TVGuideScraper:
@staticmethod @staticmethod
def _getMovies(day='', date=datetime.date.today()): def _getMovies(day='', date=datetime.date.today()):
logging.info('Connecting to {}'.format(TVGuideScraper.TV_GUIDE_URL)) url = TVGuideScraper.TV_GUIDE_URL.format(day)
r = requests.get(TVGuideScraper.TV_GUIDE_URL.format(day)) logging.info('Connecting to {}'.format(url))
r = requests.get(url)
r.raise_for_status() r.raise_for_status()
html = BeautifulSoup(r.text, 'html.parser') html = BeautifulSoup(r.text, 'html.parser')
movies = [] movies = []
for channel in html.select('.bloc_cnt'): for channel in html.select('.tab_grille'):
if len(channel.select('em')): for movietag in channel.select('.cat-film'):
for movietag in channel.find_all(TVGuideScraper._tag_is_film): try:
movie = Movie() movie = Movie()
movie.title = \ movie.title = movietag.select('a.titre')[0].string
movietag.select('.texte_titre a')[0]['title'] movie.genre = 'Film' # Genre is not available
movie.genre = movietag.select('.texte_cat a')[0].string movie.channel = channel.select('.logo_chaine_g img')[0]\
movie.channel = channel.select('em')[0]\ ['alt'].replace('Programme ','')
.string.replace('Programme ', '')
movie.day = day.title() movie.day = day.title()
movie.date = datetime.date.strftime(date, '%Y-%m-%d') movie.date = datetime.date.strftime(date, '%Y-%m-%d')
movie.start_time = datetime.datetime.strptime( movie.start_time = datetime.datetime.strptime(
'{} {}'.format( '{} {}'.format(
movie.date, movie.date,
movietag.select('.horaire')[0].string movietag.select('span.t16')[0].string
), ),
'%Y-%m-%d %H:%M' '%Y-%m-%d %H.%M'
) )
duration = TVGuideScraper._parse_duration( duration = TVGuideScraper._parse_duration(
movietag.select('.texte_cat')[0] re.search(r'\((.*) mn\)',movietag.text).group(1)
.contents[1].strip(' \n\t()')
) )
movie.end_time = movie.start_time + duration movie.end_time = movie.start_time + duration
logging.debug('Found movie: {0!r}'.format(movie)) logging.debug('Found movie: {0!r}'.format(movie))
movies.append(movie) movies.append(movie)
except:
logging.warning('Error parsing movie from tag: {0!r}'.format(movietag))
return movies return movies
@staticmethod @staticmethod
def _tag_is_film(tag): def _tag_is_movie(tag):
""" """
Helper to check if a tag is a film Helper to check if a tag is a movie
""" """
return ( return (
tag.has_attr('data-nature') tag.has_attr('data-nature')
@ -121,18 +124,12 @@ class TVGuideScraper:
@staticmethod @staticmethod
def _parse_duration(text): def _parse_duration(text):
match = re.match(r"((?P<hours>\d+)h)?(?P<minutes>\d+)mn", text) minutes = int(text)
if not match: return datetime.timedelta(minutes=minutes)
error = "Could not parse duration '{}'".format(text)
logging.error(error)
raise ValueError(error)
hours = int(match.group('hours')) if match.group('hours') else 0
minutes = int(match.group('minutes'))
return datetime.timedelta(hours=hours, minutes=minutes)
class FreeboxMoviePlanner: class FreeboxMoviePlanner:
def __init__(self, movies): def __init__(self, movies, excluded_channels=[], excluded_directory=[]):
logging.debug('Opening config file: config.json') logging.debug('Opening config file: config.json')
with open('config.json') as config_file: with open('config.json') as config_file:
self.config = json.load(config_file) self.config = json.load(config_file)
@ -140,18 +137,21 @@ class FreeboxMoviePlanner:
self.createAuthenticationToken() self.createAuthenticationToken()
tmdbsimple.API_KEY = self.config['tmdb-api'] tmdbsimple.API_KEY = self.config['tmdb-api']
self.movies = movies self.movies = movies
self.excluded_directory = excluded_directory
logging.info('Opening Freebox session') logging.info('Opening Freebox session')
self.freebox = Fbx(nomdns=True) self.freebox = Fbx()
self.freebox.mksession( self.freebox.mksession(
app_id='FreeboxMoviePlanner', app_id='FreeboxMoviePlanner',
token=self.config['freebox-session-token'] token=self.config['freebox-session-token']
) )
self.getListOfAvailableChannels() self.getListOfAvailableChannels(excluded_channels)
self.excludeUnavailableChannels() self.excludeUnavailableChannels()
self.excludeTelevisionFilm() self.excludeTelevisionMovie()
self.findMoviesOnTMDB() self.findMoviesOnTMDB()
self.excludeBadRatings() self.excludeBadRatings()
for directory in self.excluded_directory:
self.excludeLocalMovies(directory)
self.askForUserSelection() self.askForUserSelection()
self.excludeNotSelected() self.excludeNotSelected()
self.programMovies() self.programMovies()
@ -159,7 +159,7 @@ class FreeboxMoviePlanner:
def createAuthenticationToken(self): def createAuthenticationToken(self):
logging.info('Creating authentication token') logging.info('Creating authentication token')
self.freebox = Fbx(nomdns=True) self.freebox = Fbx()
hostname = socket.gethostname() hostname = socket.gethostname()
print("You don't seem to have an authentication token.") print("You don't seem to have an authentication token.")
print("I will now atempt to create one.") print("I will now atempt to create one.")
@ -178,12 +178,17 @@ class FreeboxMoviePlanner:
result += '>' result += '>'
return result return result
def getListOfAvailableChannels(self): def getListOfAvailableChannels(self, excluded_channels):
logging.info('Getting the list of available channels') logging.info('Getting the list of available channels')
self.channels = {} self.channels = {}
for channel in self.freebox.Tv.Getting_the_list_of_channels().values(): for channel in self.freebox.Tv.Getting_the_list_of_channels().values():
if channel['available']: if channel['available']:
self.channels[channel['name']] = channel['uuid'] if channel['name'] in excluded_channels:
logging.debug(
"Excluding '{}'".format(channel['name'])
)
else:
self.channels[channel['name'].lower()] = channel['uuid']
else: else:
logging.debug("Dropping '{}'".format(channel['name'])) logging.debug("Dropping '{}'".format(channel['name']))
logging.debug('Got the following channels: {}'.format(self.channels)) logging.debug('Got the following channels: {}'.format(self.channels))
@ -217,6 +222,19 @@ class FreeboxMoviePlanner:
float(movie.rating) >= self.config['minimum-rating'] float(movie.rating) >= self.config['minimum-rating']
movie.url = 'https://www.themoviedb.org/movie/{}?language={}' \ movie.url = 'https://www.themoviedb.org/movie/{}?language={}' \
.format(movie.tmdb_id, self.config['tmdb-language']) .format(movie.tmdb_id, self.config['tmdb-language'])
try:
movie.year = datetime.datetime.strptime(
tmdb_details['release_date'], '%Y-%m-%d'
).year
except (ValueError, KeyError):
logging.warning(
"No release date for '{!r}'".format(movie)
)
pass
else:
logging.warning(
"'{!r}' not found on TMDB!".format(movie)
)
def _findMovieOnTMDB(self, movie): def _findMovieOnTMDB(self, movie):
logging.info("Searching for '{}' on TMDB".format(movie)) logging.info("Searching for '{}' on TMDB".format(movie))
@ -228,7 +246,7 @@ class FreeboxMoviePlanner:
)) ))
return search.results[0] return search.results[0]
else: else:
logging.warning("'{}' not found on TMDB!".format(movie)) logging.info("'{}' not found!".format(movie))
return [] return []
def excludeBadRatings(self): def excludeBadRatings(self):
@ -242,15 +260,25 @@ class FreeboxMoviePlanner:
logging.info('Dropping movies on unavailable channels: {}'.format( logging.info('Dropping movies on unavailable channels: {}'.format(
[m for m in self.movies if m.channel not in self.channels] [m for m in self.movies if m.channel not in self.channels]
)) ))
self.movies = [m for m in self.movies if m.channel in self.channels] self.movies = [m for m in self.movies if m.channel.lower() in self.channels]
logging.debug('Kept {}'.format(self.movies)) logging.debug('Kept {}'.format(self.movies))
def excludeTelevisionFilm(self): def excludeTelevisionMovie(self):
logging.info('Dropping television films') logging.info('Dropping television movies')
self.movies = [ self.movies = [
m for m in self.movies if not m.genre.startswith("Téléfilm") m for m in self.movies if not m.genre.startswith("Téléfilm")
] ]
def excludeLocalMovies(self, directory):
(_, _, filenames) = next(os.walk(directory))
clean = lambda t: re.sub(r"( :|\?)", "", t)
logging.warning('Dropping movies already recorded: {}'.format(
[m for m in self.movies if clean(m.title)+'.m2ts' in filenames]
))
self.movies = [
m for m in self.movies if clean(m.title)+'.m2ts' not in filenames
]
def excludeNotSelected(self): def excludeNotSelected(self):
self.movies = [m for m in self.movies if m.user_selected] self.movies = [m for m in self.movies if m.user_selected]
@ -270,19 +298,21 @@ class FreeboxMoviePlanner:
def checkForConflicts(self): def checkForConflicts(self):
programmed_movies = self.freebox.Pvr.Getting_the_list_of_precords() programmed_movies = self.freebox.Pvr.Getting_the_list_of_precords()
conflicting_movies = [m for m in programmed_movies if m['conflict']] if programmed_movies:
if conflicting_movies: conflicting_movies = [m for m in programmed_movies if m['conflict']]
print( if conflicting_movies:
"\n" print(
"!!!!!!!!!\n" "\n"
"!Warning!\n" "!!!!!!!!!\n"
"!!!!!!!!!\n" "!Warning!\n"
"Conflicting records detected, please " "!!!!!!!!!\n"
"check your Freebox interface" "Conflicting records detected, please "
) "check your Freebox interface\n"
logging.info("Conflicting records detected '{}'".format( "http://192.168.1.254/#Fbx.os.app.pvr.app"
conflicting_movies )
)) logging.info("Conflicting records detected '{}'".format(
conflicting_movies
))
if __name__ == '__main__': if __name__ == '__main__':
@ -298,11 +328,45 @@ if __name__ == '__main__':
action='store_true', action='store_true',
help='Search movies for current day only instead of a full week' help='Search movies for current day only instead of a full week'
) )
parser.add_argument(
'-l', '--log',
action='store_true',
help='Display more log messages'
)
parser.add_argument(
'-b', '--debug',
action='store_true',
help='Display even more log messages'
)
parser.add_argument(
'-e', '--exclude',
action='append',
default=[],
help='Exclude the following Channel'
)
parser.add_argument(
'-x', '--exclude-directory',
action='append',
default=[],
help='''Do not display movies available in the following directory.
This will prevent you from recording the same movie multiple
times.'''
)
args = parser.parse_args() args = parser.parse_args()
print("Working the magic, please wait…") print("Working the magic, please wait…")
if args.debug:
logging.getLogger().setLevel(logging.DEBUG)
elif args.log:
logging.getLogger().setLevel(logging.INFO)
if args.day: if args.day:
movies = TVGuideScraper._getMovies() movies = TVGuideScraper._getMovies()
else: else:
movies = TVGuideScraper.findAllMovies() movies = TVGuideScraper.findAllMovies()
fmp = FreeboxMoviePlanner(movies)
fmp = FreeboxMoviePlanner(
movies,
excluded_channels=args.exclude,
excluded_directory=args.exclude_directory
)
input("Press Enter to continue...")

View File

@ -5,11 +5,11 @@ name = "pypi"
[packages] [packages]
requests = "*" requests = "*"
beautifulsoup4 = "*"
tmdbsimple = "*" tmdbsimple = "*"
zeroconf = "*" zeroconf = "*"
bs4 = "*"
[dev-packages] [dev-packages]
[requires] [requires]
python_version = "3.7" python_version = "3.9"

76
Pipfile.lock generated
View File

@ -1,11 +1,11 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "b03fc445dafa601842507fc39fc83f3041132934f9ceb06ca0d037b361421c02" "sha256": "90b0ce807d75e401aca384343417ee0c7ba3250bb05b846bd886d476cf56d1b1"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
"python_version": "3.7" "python_version": "3.9"
}, },
"sources": [ "sources": [
{ {
@ -18,77 +18,87 @@
"default": { "default": {
"beautifulsoup4": { "beautifulsoup4": {
"hashes": [ "hashes": [
"sha256:5279c36b4b2ec2cb4298d723791467e3000e5384a43ea0cdf5d45207c7e97169", "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35",
"sha256:6135db2ba678168c07950f9a16c4031822c6f4aec75a65e0a97bc5ca09789931", "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25",
"sha256:dcdef580e18a76d54002088602eba453eec38ebbcafafeaabd8cab12b6155d57" "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"
],
"version": "==4.9.3"
},
"bs4": {
"hashes": [
"sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a"
], ],
"index": "pypi", "index": "pypi",
"version": "==4.8.1" "version": "==0.0.1"
}, },
"certifi": { "certifi": {
"hashes": [ "hashes": [
"sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee",
"sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef" "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"
], ],
"version": "==2019.9.11" "version": "==2021.5.30"
}, },
"chardet": { "charset-normalizer": {
"hashes": [ "hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", "sha256:88fce3fa5b1a84fdcb3f603d889f723d1dd89b26059d0123ca435570e848d5e1",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" "sha256:c46c3ace2d744cfbdebceaa3c19ae691f53ae621b39fd7570f59d14fb7f2fd12"
], ],
"version": "==3.0.4" "markers": "python_version >= '3'",
"version": "==2.0.3"
}, },
"idna": { "idna": {
"hashes": [ "hashes": [
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a",
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"
], ],
"version": "==2.8" "markers": "python_version >= '3'",
"version": "==3.2"
}, },
"ifaddr": { "ifaddr": {
"hashes": [ "hashes": [
"sha256:c19c64882a7ad51a394451dabcbbed72e98b5625ec1e79789924d5ea3e3ecb93" "sha256:1f9e8a6ca6f16db5a37d3356f07b6e52344f6f9f7e806d618537731669eb1a94",
"sha256:d1f603952f0a71c9ab4e705754511e4e03b02565bc4cec7188ad6415ff534cd3"
], ],
"version": "==0.1.6" "version": "==0.1.7"
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.22.0" "version": "==2.26.0"
}, },
"soupsieve": { "soupsieve": {
"hashes": [ "hashes": [
"sha256:605f89ad5fdbfefe30cdc293303665eff2d188865d4dbe4eb510bba1edfbfce3", "sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc",
"sha256:b91d676b330a0ebd5b21719cb6e9b57c57d433671f65b9c28dd3461d9a1ed0b6" "sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b"
], ],
"version": "==1.9.4" "markers": "python_version >= '3.0'",
"version": "==2.2.1"
}, },
"tmdbsimple": { "tmdbsimple": {
"hashes": [ "hashes": [
"sha256:3383780ae40fbc2b5197feb06496f81f3a5c0ff3336a2a971a51e638fb10c895", "sha256:2f6f4b762e07e71e222b3200e3b83c4fce8ff61892402a60be925f79d49ab7fd",
"sha256:cd8c5bc3599e803f1f908339b3d010e138722d147f10ed410b3205c72db0c00c" "sha256:c882112396634bc8d42689fdf347e4a230f6897e5c423fd4d3ab1411dcb4b98f"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.2.0" "version": "==2.8.0"
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
"sha256:3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398", "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4",
"sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86" "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"
], ],
"version": "==1.25.6" "version": "==1.26.6"
}, },
"zeroconf": { "zeroconf": {
"hashes": [ "hashes": [
"sha256:21d02538ff52fc572e1d785c692b97b8d4374623cb95d593cc06ab92bd5aaf61", "sha256:5b91d4d93c225f60dbb37915f63c2d1b445b7b39ba17f8da42f51c373d866af4",
"sha256:e0c333b967c48f8b2e5cc94a1d4d28893023fb06dfd797ee384a94cdd1d0eef5" "sha256:75e6616401f6d4a51f522f558b5d8a329da1407adc3e876134a4533e29acd279"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.23.0" "version": "==0.32.1"
} }
}, },
"develop": {} "develop": {}

View File

@ -12,7 +12,7 @@ d'enregistrements avec votre *Freebox*.
Concrètement, l'outil effectue les opérations suivantes : Concrètement, l'outil effectue les opérations suivantes :
* Récupérer les film de la semaine à partir du [planning *Télé 7 Jours*](https://www.programme-television.org/?bouquet=free) * Récupérer les film de la semaine à partir du [planning *NouvelObs*](https://programme-tv.nouvelobs.com/programme-free/categorie-film/)
* Retirer les chaînes auxquelles vous n'avez pas accès * Retirer les chaînes auxquelles vous n'avez pas accès
* Trouver les notes des films sur [*TMDb*](https://www.themoviedb.org/) (*The Movie Database*) * Trouver les notes des films sur [*TMDb*](https://www.themoviedb.org/) (*The Movie Database*)
* Exclure les films en dessous d'une certaine note (paramétrable) * Exclure les films en dessous d'une certaine note (paramétrable)
@ -20,15 +20,18 @@ Concrètement, l'outil effectue les opérations suivantes :
* Planifier les enregistrements sur votre *Freebox*. * Planifier les enregistrements sur votre *Freebox*.
* Vous prévenir si des conflits sont identifiés pour que vous les résolviez depuis l'interface de votre *Freebox*. * Vous prévenir si des conflits sont identifiés pour que vous les résolviez depuis l'interface de votre *Freebox*.
![FreeboxMoviePlanner en action](https://djib.fr/djib/FreeboxMoviePlanner/raw/branch/master/screenshot.png) ![FreeboxMoviePlanner en action](https://git.djib.fr/djib/FreeboxMoviePlanner/raw/branch/master/screenshot.png)
Installation Installation
--- ---
### À partir de l'exécutable ### À partir de l'exécutable
[Téléchargez](https://djib.fr/djib/FreeboxMoviePlanner/releases) le fichier qui correspond à votre système d'exploitation (Linux ou Windows) dans le répertoire `dist` ainsi que le fichier `config.template.json`. ~~[Téléchargez](https://git.djib.fr/djib/FreeboxMoviePlanner/releases) le fichier qui correspond à votre système d'exploitation (Linux ou Windows).
Extrayez ensuite ce fichier à l'emplacement de votre choix.~~
Lexécutable nest plus valable car il utilisait une ancienne version du site de Télé 7 Jours. Si une nouvelle version vous intéresse, nhésitez pas à [le demander dans un ticket](https://git.djib.fr/djib/FreeboxMoviePlanner/issues).
### À partir des sources ### À partir des sources
Pour utiliser **FreeboxMoviePlanner** à partir des sources, il vous faut Python, ainsi que les 3 packages suivants (à installer par exemple avec `pip install [nom du packet]` : Pour utiliser **FreeboxMoviePlanner** à partir des sources, il vous faut Python, ainsi que les 3 packages suivants (à installer par exemple avec `pip install [nom du packet]`) :
* `beautifulsoup4` * `beautifulsoup4`
* `tmdbsimple` * `tmdbsimple`
@ -36,14 +39,14 @@ Pour utiliser **FreeboxMoviePlanner** à partir des sources, il vous faut Python
Il vous faut ensuite récupérer le code du projet, le plus simple étant en clonant le dépôt git. Il vous faut ensuite récupérer le code du projet, le plus simple étant en clonant le dépôt git.
```bash ```bash
git clone --recurse-submodules https://djib.fr/djib/FreeboxMoviePlanner.git git clone --recurse-submodules https://git.djib.fr/djib/FreeboxMoviePlanner.git
``` ```
Configuration Configuration
--- ---
Ensuite, il vous faut créer un fichier de conf `config.json`. Vous pouvez tout simplement renommer le fichier `config.template.json`. Ensuite, il vous faut créer un fichier de conf `config.json`. Vous pouvez tout simplement renommer le fichier `config.template.json`.
Dans ce fichier vous devrez renseigner une clef API TMDb. Pour cela vous aurez besoin d'ouvrir un compte sur leur site et de [demander une clef API](https://www.themoviedb.org/settings/api). Dans ce fichier vous devrez renseigner une clef API TMDb. Pour cela vous aurez besoin d'[ouvrir un compte](https://www.themoviedb.org/account/signup) sur leur site et de [demander une clef API](https://www.themoviedb.org/settings/api).
Le fichier de config vous permet aussi de configurer la marge avant et après chaque enregistrement, ou la note en dessous de laquelle les films ne s'afficheront pas. Le fichier de config vous permet aussi de configurer la marge avant et après chaque enregistrement, ou la note en dessous de laquelle les films ne s'afficheront pas.
@ -51,5 +54,27 @@ Exécution
--- ---
L'outil devrait vous afficher rapidement le petit texte `Working the magic, please wait…` puis, après environ une minute (ou plus si votre réseau est lent), le détail des films. L'outil devrait vous afficher rapidement le petit texte `Working the magic, please wait…` puis, après environ une minute (ou plus si votre réseau est lent), le détail des films.
À la première exécution, l'outil vous demandera aussi d'autoriser le script sur votre Freebox (*Please go to your Freebox and accept the authentication.*) À la première exécution, l'outil vous demandera aussi d'autoriser le script sur votre Freebox (`Please go to your Freebox and accept the authentication.`)
À ce moment, l'affichage de votre boîtier *Freebox* vous demandera alors de valider cette demande d'authentification. Vous devez donc physiquement valider l'accès à partir de votre box (sur la V6, il s'agit de presser la flèche de droite). À ce moment, l'affichage de votre boîtier *Freebox* vous demandera de valider cette demande d'authentification. Vous devez donc physiquement valider l'accès à partir de votre box (sur la V6, il s'agit de presser la flèche de droite).
Notez que les films de *Canal+* s'afficheront par défaut. La *Freebox* indique que la chaîne est disponible, probablement car elle est parfois visible en clair. Vous pouvez forcer son exclusion avec le paramètre `-e Canal+`.
Enjoy!
Documentation
---
```shell
usage: FreeboxMoviePlanner.py [-h] [-d] [-l] [-e EXCLUDE] [-x EXCLUDE_DIRECTORY]
Schedule movie recordings on your Freebox
optional arguments:
-h, --help show this help message and exit
-d, --day Search movies for current day only instead of a full week
-l, --log Display more log messages
-b, --debug Display even more log messages
-e EXCLUDE, --exclude EXCLUDE
Exclude the following Channel
-x EXCLUDE_DIRECTORY, --exclude-directory EXCLUDE_DIRECTORY
Do not display movies available in the following directory. This will prevent you from recording the same movie multiple times.
```

2
pyfbx

Submodule pyfbx updated: 69ae89806d...5cbc535286