diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..01a4ef3
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,5 @@
+[flake8]
+max-complexity = 10
+max-line-length = 127
+exclude = .git,__pycache__
+per-file-ignores = __init__.py:F401
diff --git a/.gitignore b/.gitignore
index 6b253df..20b70cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
__pycache__/
-.vscode/
\ No newline at end of file
+.vscode/
+api_cache/
diff --git a/LICENSE b/LICENSE
index 6fd18d8..d36798c 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2021 Crinibus
+Copyright (c) 2022 Crinibus
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 43e312f..5a60b9a 100644
--- a/README.md
+++ b/README.md
@@ -7,11 +7,15 @@ A program that gives the user a list over what comes in tv*.
+Requires ```python3.9+```
+
+
+
# How to use
An example on how to use this program:
```
-python3 main.py -c dr1 -c tv2 --all
+python3 main.py -c dr1 tv2 --all
```
This prints all the programs that run today for the channels DR1 and TV2.
@@ -19,7 +23,7 @@ This prints all the programs that run today for the channels DR1 and TV2.
Another example:
```
-python3 main.py -c dr1 -c tv2 -t 20:00 -d 1
+python3 main.py -c dr1 tv2 -t 20:00 -d 1
```
This prints only the tv-shows that start or is running at 8 pm the next day on the channels DR1 and TV2.
@@ -45,7 +49,6 @@ This prints the programs (with categories) that is currently running on the defa
- ```-v``` or ```--verbose```
- ```-s [search-term]``` or ```--search [search-term]```
- ```--default-channels [channel]```
-- ```--default-space-seperator [space-seperator]```
- ```--justify-length [int]```
@@ -53,21 +56,23 @@ This prints the programs (with categories) that is currently running on the defa
## --channel
-By using the flag ```-c``` or ```--channel``` you specify which channels you want the program to show tv-shows from. Replace "[channel]" with wanted channel, e.g. "dr1".
-You can specify multiple channels just by using the ```-c``` flag again.
+By using the flag ```-c``` or ```--channel``` you specify which channels you want the program to show tv-shows from. Replace "[channel]" with wanted channel(s), e.g. "dr1".
If no channel(s) is chosen, the default channels is used. You can change the default channels as described [here](#--default-channels)
For example:
```
python3 main.py -c [channel_1] -c [channel_2]
```
-**OBS**: when specifing channels with a space such as "TV2 News", use a dash (-) instead of a space. E.g. "TV2 News" -> "TV2-News"
+or
+```
+python3 main.py -c [channel_1] [channel_2]
+```
You can also specify "all" as the first channel to get all channels.
Examples:
```
-python3 main.py -c dr1 -c tv2 --all
+python3 main.py -c dr1 tv2 --all
```
This shows all the programs that run on DR1 and TV2 for today.
@@ -135,7 +140,7 @@ This shows all the programs that have the category "film" on the default channel
```
-python3 main.py --category film --category drama
+python3 main.py --category film drama
```
This shows all the programs that have either the category "film" or "drama" on the default channels for today.
@@ -178,7 +183,7 @@ This shows all the programs on the default channels for today that have the word
```
-python3 main.py --search avis --search vejr
+python3 main.py --search avis vejr
```
This shows all the programs on the default channels for today that have either the words "avis" or "vejr" in the title.
@@ -197,18 +202,6 @@ This changes the default channels to "DR1", "TV2" and "CANAL 9".
-## --default-space-seperator
-
-
-Example:
-```
-python3 main.py --default-space-seperator -
-```
-This changes the default space seperator to the sign "-".
-
-
-
-
## --justify-length
diff --git a/main.py b/main.py
index fffd68b..39e1144 100644
--- a/main.py
+++ b/main.py
@@ -1,106 +1,22 @@
-# from format import Format
-# from argument import argparse_setup
-# from API import API
-# from Config import Config
import tvguide as tv
-def print_currently_running(data_source: dict, user_channels: list) -> None:
- channels_string = ", ".join(user_channels).upper()
-
- print(f"\n----- Showing currently running programs for: {channels_string} -----", end="")
- for channel in user_channels:
- data_source[channel].print_currently_running()
-
-
-def print_program_times(data_source: dict, user_channels: list, user_times: list) -> None:
- channels_string = ", ".join(user_channels).upper()
- user_times_string = ", ".join(user_times)
-
- print(f"\n----- Showing programs that is running at: {user_times_string} for: {channels_string} -----", end="")
- for channel in user_channels:
- data_source[channel].print_times(user_times)
-
-
-def print_program_categories(data_source: dict, user_channels: list, user_categories: list) -> None:
- user_categories_string = ", ".join(user_categories)
-
- print(f"\n----- Searching for categories: {user_categories_string} -----", end="")
- for channel in user_channels:
- data_source[channel].print_categories(user_categories)
-
-
-def print_program_searches(data_source: dict, user_channels: list, user_searches: list) -> None:
- user_search_string = tv.Format.user_search(", ".join(user_searches))
-
- print(f"\n----- Searching for keywords: {user_search_string} -----", end="")
- for channel in user_channels:
- data_source[channel].print_searches(user_searches)
-
-
-def print_program_all(data_source: dict, user_channels: list) -> None:
- user_channels_string = ", ".join(user_channels).upper()
-
- print(f"\n----- Showing all programs for: {user_channels_string} -----", end="")
- for channel in user_channels:
- data_source[channel].print_all_programs()
-
-
-def change_defaults(args, data: dict):
- if args.default_channels:
- tv.Config.change_defaults_user_channels(args.default_channels)
- default_channels_string = ", ".join(args.default_channels).upper()
- print(f"Changed default channel(s) to: {default_channels_string}")
-
- if args.default_space_seperator:
- tv.Config.change_space_seperator(args.default_space_seperator)
- print(f"Changed space seperator to: {args.default_space_seperator}")
-
- if args.justify_length:
- tv.Config.change_justify_length(args.justify_length)
- print(f"Changed justify length to: {args.justify_length}")
-
- if not args.channel:
- args.channel = tv.Config.get_defaults_user_channels()
- default_channels_string = ", ".join(args.channel).upper()
- print(f"No channel(s) chosen: using default channels ({default_channels_string})")
- elif args.channel[0].lower() == "all":
- args.channel = [channel for channel in data.keys()]
-
-
-def print_programs(args, data):
- if args.now:
- print_currently_running(data, args.channel)
-
- if args.time:
- print_program_times(data, args.channel, args.time)
-
- if args.category:
- print_program_categories(data, args.channel, args.category)
-
- if args.search:
- print_program_searches(data, args.channel, args.search)
-
- if args.all:
- print_program_all(data, args.channel)
-
-
def main():
args = tv.argparse_setup()
- api_data = tv.API.get_data(args.day)
+ tvguide = tv.TvGuide()
+
+ tvguide.parse_arguments(args)
- my_data = tv.API.format_data(api_data, args.verbose)
+ api_data = tv.ApiManager.get_data(tvguide.relative_date)
- change_defaults(args, my_data)
+ tvguide.parse_api_data(api_data)
- print_programs(args, my_data)
+ tvguide.print_programs()
if __name__ == "__main__":
try:
main()
- except KeyError:
- print("Check channel name or this scraper can't use this channel")
except KeyboardInterrupt:
print("Stopped by user")
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..dcff8a7
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,2 @@
+[tool.black]
+line-length = 127
diff --git a/requirements.txt b/requirements.txt
index 4261e68..ad40ca9 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1 +1,2 @@
-requests==2.24.0
\ No newline at end of file
+requests>=2.24.0
+requests-cache>=0.9.6
\ No newline at end of file
diff --git a/tvguide/API.py b/tvguide/API.py
deleted file mode 100644
index 03413dd..0000000
--- a/tvguide/API.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import requests
-from .const import API_LINK, REQUEST_HEADER, REQUEST_COOKIES
-from .format import Format
-from .tv import Channel
-
-
-class API:
- @staticmethod
- def get_link(relative_date: int) -> str:
- return API_LINK.replace("{date}", Format.get_specified_date(relative_date))
-
- @staticmethod
- def get_data(relative_day: int) -> dict:
- """Get formatted data from API"""
- response = requests.get(
- API.get_link(relative_day),
- headers=REQUEST_HEADER,
- cookies=REQUEST_COOKIES
- )
-
- return response.json()
-
- @staticmethod
- def format_data(api_data: dict, verbose: bool) -> dict:
- formatted_data = {}
-
- for channel in api_data:
- temp_channel = Channel(channel, verbose)
- formatted_data.update({temp_channel.name: temp_channel})
-
- return formatted_data
diff --git a/tvguide/Config.py b/tvguide/Config.py
deleted file mode 100644
index 8034bf4..0000000
--- a/tvguide/Config.py
+++ /dev/null
@@ -1,61 +0,0 @@
-from configparser import ConfigParser
-from .filemanager import Filemanager
-
-
-class Config:
- @staticmethod
- def read(file_name: str) -> ConfigParser:
- """Read user settings in {file_name}.ini"""
- config = ConfigParser()
- config.read(file_name)
- return config
-
- @staticmethod
- def write(file_name: str, config: ConfigParser) -> None:
- """Write user settings in {file_name}.ini"""
- with open(file_name, 'w') as default_file:
- config.write(default_file)
-
- @staticmethod
- def get_defaults_user_channels() -> list:
- config = Config.read(f'{Filemanager.get_root_path()}/defaults.ini')
-
- defaultChannels = config['DefaultChannels']['channels']
-
- return defaultChannels.split(',')
-
- @staticmethod
- def change_defaults_user_channels(new_defaults: list) -> None:
- config = Config.read(f'{Filemanager.get_root_path()}/defaults.ini')
-
- config['DefaultChannels']['channels'] = ','.join(new_defaults)
-
- Config.write('defaults.ini', config)
-
- @staticmethod
- def get_space_seperator() -> str:
- config = Config.read(f'{Filemanager.get_root_path()}/defaults.ini')
-
- return config['Misc']['spaceSeperator']
-
- @staticmethod
- def change_space_seperator(new_space_seperator: str) -> None:
- config = Config.read(f'{Filemanager.get_root_path()}/defaults.ini')
-
- config['Misc']['spaceSeperator'] = new_space_seperator
-
- Config.write('defaults.ini', config)
-
- @staticmethod
- def get_justify_length() -> int:
- config = Config.read(f'{Filemanager.get_root_path()}/defaults.ini')
-
- return int(config['Misc']['justifyLength'])
-
- @staticmethod
- def change_justify_length(new_length: int):
- config = Config.read(f'{Filemanager.get_root_path()}/defaults.ini')
-
- config['Misc']['justifyLength'] = new_length
-
- Config.write('defaults.ini', config)
diff --git a/tvguide/__init__.py b/tvguide/__init__.py
index 135f832..4e4a10c 100644
--- a/tvguide/__init__.py
+++ b/tvguide/__init__.py
@@ -1,4 +1,5 @@
from .format import Format
from .argument import argparse_setup
-from .API import API
-from .Config import Config
\ No newline at end of file
+from .api import ApiManager
+from .config import ConfigManager
+from .tvguide import TvGuide
diff --git a/tvguide/api.py b/tvguide/api.py
new file mode 100644
index 0000000..0d20fc5
--- /dev/null
+++ b/tvguide/api.py
@@ -0,0 +1,23 @@
+import requests
+import requests_cache
+
+from tvguide.const import API_LINK, REQUEST_HEADER, REQUEST_COOKIES
+from tvguide.format import Format
+from tvguide.tv import Channel
+
+requests_cache.install_cache(cache_name="api_cache", expire_after=120, backend="filesystem")
+
+
+class ApiManager:
+ @staticmethod
+ def get_link(relative_date: int) -> str:
+ date = Format.get_specified_date(relative_date)
+ return API_LINK.replace("{date}", date)
+
+ @staticmethod
+ def get_data(relative_day: int) -> dict:
+ """Get formatted data from API"""
+ api_link = ApiManager.get_link(relative_day)
+ response = requests.get(api_link, headers=REQUEST_HEADER, cookies=REQUEST_COOKIES)
+
+ return response.json()
diff --git a/tvguide/argument.py b/tvguide/argument.py
index 0159eb0..6a42066 100644
--- a/tvguide/argument.py
+++ b/tvguide/argument.py
@@ -6,94 +6,56 @@ def argparse_setup() -> ArgumentParser.parse_args:
parser = ArgumentParser()
parser.add_argument(
- '-c',
- '--channel',
- dest='channel',
- help='the channel with the user want to see programs from',
+ "-c",
+ "--channel",
+ dest="channels",
+ help="the channel with the user want to see programs from",
nargs="*",
action="extend",
- type=str
+ type=str,
)
parser.add_argument(
- '-t',
- '--time',
- dest='time',
- metavar='hh:mm',
+ "-t",
+ "--time",
+ dest="time",
+ metavar="hh:mm",
help='the time the program starts. E.g. "20:00". Format is: "hh:mm"',
nargs="*",
action="extend",
- type=str
+ type=str,
)
- parser.add_argument(
- '-a',
- '--all',
- help='show all programs for the chosen channel(s)',
- action='store_true'
- )
+ parser.add_argument("-a", "--all", help="show all programs for the chosen channel(s)", action="store_true")
parser.add_argument(
- '-d',
- '--day',
- help='the relative day the programs is running, default today (0)',
+ "-d",
+ "--day",
+ help="the relative day the programs is running, default today (0)",
choices=[-1, 0, 1, 2, 3, 4, 5, 6],
type=int,
- default=0
+ default=0,
)
parser.add_argument(
- '--category',
- dest='category',
- help='only show the programs with the chosen category(s)',
+ "--category",
+ dest="category",
+ help="only show the programs with the chosen category(s)",
nargs="*",
action="extend",
- type=str
+ type=str,
)
- parser.add_argument(
- '-n',
- '--now',
- help='only show the program(s) that is currently',
- action='store_true'
- )
+ parser.add_argument("-n", "--now", help="only show the program(s) that is currently", action="store_true")
- parser.add_argument(
- '-v',
- '--verbose',
- help='show categories when using --all and --time',
- action='store_true'
- )
+ parser.add_argument("-v", "--verbose", help="show categories when using --all and --time", action="store_true")
parser.add_argument(
- '--default-channels',
- dest='default_channels',
- help='change default channels to the chosen channel(s)',
- nargs='*'
+ "--default-channels", dest="default_channels", help="change default channels to the chosen channel(s)", nargs="*"
)
- parser.add_argument(
- '--default-space-seperator',
- dest='default_space_seperator',
- help='change space seperator sign',
- type=str
- )
+ parser.add_argument("--justify-length", dest="justify_length", help="change justify length", type=str)
- parser.add_argument(
- '--justify-length',
- dest='justify_length',
- help='change justify length',
- type=str
- )
-
- parser.add_argument(
- '-s',
- '--search',
- dest='search',
- help='search for programs',
- nargs="*",
- action="extend",
- type=str
- )
+ parser.add_argument("-s", "--search", dest="search", help="search for programs", nargs="*", action="extend", type=str)
return parser.parse_args()
diff --git a/tvguide/config.py b/tvguide/config.py
new file mode 100644
index 0000000..afdbc75
--- /dev/null
+++ b/tvguide/config.py
@@ -0,0 +1,61 @@
+from typing import List
+from configparser import ConfigParser
+from functools import cache
+
+from tvguide.filemanager import Filemanager
+
+
+class ConfigManager:
+ @staticmethod
+ def read(file_name: str) -> ConfigParser:
+ """Read user settings in {file_name}.ini"""
+ config = ConfigParser()
+ config.read(file_name)
+ return config
+
+ @staticmethod
+ def write(file_name: str, config: ConfigParser) -> None:
+ """Write user settings in {file_name}.ini"""
+ with open(file_name, "w") as default_file:
+ config.write(default_file)
+
+ @staticmethod
+ @cache
+ def read_defaults_config_file() -> ConfigParser:
+ return ConfigManager.read(Filemanager.defaults_ini_path)
+
+ @staticmethod
+ def get_defaults_user_channels() -> List[str]:
+ config = ConfigManager.read_defaults_config_file()
+
+ defaultChannels = config["DefaultChannels"]["channels"]
+
+ return defaultChannels.split(",")
+
+ @staticmethod
+ def change_defaults_user_channels(new_defaults: List[str]) -> None:
+ config = ConfigManager.read_defaults_config_file()
+
+ config["DefaultChannels"]["channels"] = ",".join(new_defaults)
+
+ ConfigManager.write(Filemanager.defaults_ini_path, config)
+
+ @staticmethod
+ def get_justify_length() -> int:
+ config = ConfigManager.read_defaults_config_file()
+
+ return int(config["Misc"]["justifyLength"])
+
+ @staticmethod
+ def change_justify_length(new_length: int):
+ config = ConfigManager.read_defaults_config_file()
+
+ config["Misc"]["justifyLength"] = new_length
+
+ ConfigManager.write(Filemanager.defaults_ini_path, config)
+
+ @staticmethod
+ def get_timezone() -> str:
+ config = ConfigManager.read_defaults_config_file()
+
+ return config["Timezone"]["timezone"]
diff --git a/tvguide/const.py b/tvguide/const.py
index 41f086f..67701d7 100644
--- a/tvguide/const.py
+++ b/tvguide/const.py
@@ -1,6 +1,8 @@
API_LINK = "https://tvtid-api.api.tv2.dk/api/tvtid/v1/epg/dayviews/{date}?ch=1&ch=2&ch=3&ch=4&ch=5&ch=6&ch=7&ch=8&ch=9&ch=10&ch=10&ch=11&ch=12&ch=14&ch=15&ch=19&ch=20&ch=25&ch=26&ch=31&ch=37&ch=39&ch=70&ch=71&ch=74&ch=77&ch=93&ch=94&ch=111&ch=116&ch=117&ch=118&ch=129&ch=130&ch=133&ch=135&ch=136&ch=142&ch=147&ch=153&ch=156&ch=157&ch=161&ch=162&ch=163&ch=164&ch=165&ch=174&ch=181&ch=185&ch=188&ch=191&ch=201&ch=215&ch=218&ch=219&ch=221&ch=240&ch=248&ch=570&ch=687&ch=689&ch=10061&ch=10066&ch=10070&ch=10093&ch=10104&ch=10111&ch=10145&ch=10153&ch=10159&ch=10262&ch=10341&ch=10435&ch=10455&ch=10510&ch=10511&ch=10621&ch=11572&ch=11610&ch=11611&ch=11612&ch=11613&ch=11614&ch=11615&ch=11616&ch=11617&ch=12034&ch=12396&ch=12566&ch=12697&ch=12948&ch=15032&ch=15049&ch=15129&ch=2147483561&ch=2147483566&ch=2147483567&ch=2147483568"
-REQUEST_HEADER = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36"}
+REQUEST_HEADER = {
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36"
+}
REQUEST_COOKIES = {"cookies_are": "working"}
@@ -110,98 +112,98 @@
"3": "tv2",
"5": "tv3",
"2": "dr2",
- "31": "tv2-charlie",
- "133": "tv2-news",
- "7": "kanal-5",
- "6": "tv3-plus",
- "4": "tv2-zulu",
- "10153": "dr-ramasjang",
- "8": "kanal-4",
- "77": "tv2-sport",
- "2147483561": "tv2-sport-x",
- "156": "tv3-sport",
- "10093": "tv3-puls",
+ "31": "tv2 charlie",
+ "133": "tv2 news",
+ "7": "kanal 5",
+ "6": "tv3 plus",
+ "4": "tv2 zulu",
+ "10153": "dr ramasjang",
+ "8": "kanal 4",
+ "77": "tv2 sport",
+ "2147483561": "tv2 sport x",
+ "156": "tv3 sport",
+ "10093": "tv3 puls",
"10066": "6eren",
- "14": "disney-channel",
- "12566": "tv2-fri",
- "10111": "canal-9",
- "70": "discovery-channel",
+ "14": "disney channel",
+ "12566": "tv2 fri",
+ "10111": "canal 9",
+ "70": "discovery channel",
"118": "tlc",
"153": "nickelodeon",
- "94": "national-geographic-channel",
- "12948": "tv3-max",
+ "94": "national geographic channel",
+ "12948": "tv3 max",
"185": "cartoon",
- "157": "disney-junior",
+ "157": "disney junior",
"15": "dk4",
"71": "mtv",
- "93": "animal-planet",
- "15049": "investigation-discovery",
+ "93": "animal planet",
+ "15049": "investigation discovery",
"219": "vh1",
- "37": "eurosport-2",
+ "37": "eurosport 2",
"248": "boomerang",
- "9": "viasat-film",
- "10": "viasat-film-action",
- "11": "viasat-film-hits",
- "12": "viasat-film-family",
- "19": "viasat-explorer",
- "20": "viasat-nature",
- "25": "c-more-first",
- "26": "c-more-hits",
- "39": "viasat-history",
- "74": "disney-xd",
- "111": "tv4-sverige",
- "116": "discovery-world",
+ "9": "viasat film",
+ "10": "viasat film action",
+ "11": "viasat film hits",
+ "12": "viasat film family",
+ "19": "viasat explorer",
+ "20": "viasat nature",
+ "25": "c more first",
+ "26": "c more hits",
+ "39": "viasat history",
+ "74": "disney xd",
+ "111": "tv4 sverige",
+ "116": "discovery world",
"129": "nrk2",
"130": "nrk1",
"135": "svt1",
"136": "sv2",
- "142": "tv2-norge",
- "147": "discovery-hd-showcase",
+ "142": "tv2 norge",
+ "147": "discovery hd showcase",
"161": "rtl",
"162": "ard",
"163": "zdf",
"164": "3sat",
- "165": "viasat-golf",
- "174": "eurosport-1",
+ "165": "viasat golf",
+ "174": "eurosport 1",
"181": "cnn",
"188": "ndr",
- "191": "bbc-world",
- "201": "c-more-series",
- "215": "travel-channel",
+ "191": "bbc world",
+ "201": "c more series",
+ "215": "travel channel",
"218": "vox",
- "221": "rtl-2",
- "240": "super-rtl",
- "570": "paramount-network",
+ "221": "rtl 2",
+ "240": "super rtl",
+ "570": "paramount network",
"687": "xee",
- "689": "viasat-ultra",
- "10061": "bbc-earth",
- "10070": "viasat-series",
+ "689": "viasat ultra",
+ "10061": "bbc earth",
+ "10070": "viasat series",
"10104": "arte",
- "10145": "sf-kanalen",
+ "10145": "sf kanalen",
"10159": "history",
- "10262": "kanal-hovedstaden",
+ "10262": "kanal hovedstaden",
"10341": "folketinget",
"10435": "tnt",
- "10455": "nickelodeon-junior",
+ "10455": "nickelodeon junior",
"10510": "sat1",
"10511": "prosieben",
"10621": "sport1",
- "11572": "national-geographic-wild",
- "11610": "tv2-nord-salto",
- "11611": "tv-midt-vest",
- "11612": "tv2-østjylland",
- "11613": "tv2-øst",
- "11614": "tv-syd",
- "11615": "tv-fyn",
+ "11572": "national geographic wild",
+ "11610": "tv2 nord salto",
+ "11611": "tv midt vest",
+ "11612": "tv2 østjylland",
+ "11613": "tv2 øst",
+ "11614": "tv syd",
+ "11615": "tv fyn",
"11616": "lorry",
- "11617": "tv2-bornholm",
- "12034": "tv3-sport-2-hd",
- "12396": "bbc-brit",
- "12697": "tv5-monde-europe",
- "15032": "national-geographic-people",
- "15129": "comedy-central",
- "2147483566": "cs-go",
- "2147483567": "zulu-comedy",
+ "11617": "tv2 bornholm",
+ "12034": "tv3 sport 2 hd",
+ "12396": "bbc brit",
+ "12697": "tv5 monde europe",
+ "15032": "national geographic people",
+ "15129": "comedy central",
+ "2147483566": "cs go",
+ "2147483567": "zulu comedy",
"2147483568": "oiii",
- "117": "discovery-science",
+ "117": "discovery science",
}
diff --git a/tvguide/defaults.ini b/tvguide/defaults.ini
index e50c2fd..5d5d6bc 100644
--- a/tvguide/defaults.ini
+++ b/tvguide/defaults.ini
@@ -7,3 +7,6 @@ channels = dr1,tv2
spaceSeperator = *
; Change justify length here or use flag --justify-length
justifyLength = 45
+
+[Timezone]
+timezone = Europe/Copenhagen
diff --git a/tvguide/filemanager.py b/tvguide/filemanager.py
index 8541b91..4581323 100644
--- a/tvguide/filemanager.py
+++ b/tvguide/filemanager.py
@@ -1,7 +1,7 @@
import pathlib
+
class Filemanager:
- @staticmethod
- def get_root_path():
- """Return root path of this repository"""
- return pathlib.Path(__file__).parent.absolute()
+ # root path of this repository
+ root_path = pathlib.Path(__file__).parent.parent.absolute()
+ defaults_ini_path = f"{root_path}/tvguide/defaults.ini"
diff --git a/tvguide/format.py b/tvguide/format.py
index a4a76a1..630b219 100644
--- a/tvguide/format.py
+++ b/tvguide/format.py
@@ -1,14 +1,8 @@
from datetime import datetime, timedelta, timezone
-from .Config import Config
import pytz
class Format:
- @staticmethod
- def channel_name(name: str) -> str:
- name = name.replace("-", " ")
- return name.upper()
-
@staticmethod
def user_time(time: str) -> int:
return int(time.replace(":", ""))
@@ -18,29 +12,21 @@ def get_specified_date(relative_date: int) -> str:
"""Convert relative date to real date, so that if argument 'relative_date' is 1, it get converted to tomorrow"""
date = datetime.today()
date += timedelta(days=relative_date)
- return date.strftime('%Y-%m-%d')
+ return date.strftime("%Y-%m-%d")
@staticmethod
def convert_unix_time(unix_time: int, toShow: bool):
"""Convert UNIX times to hour and minutes, e.g.: 1604692800 -> 2000 or 20:00\n
- UNIX time is converted to timezone: Europe/Copenhagen"""
+ UNIX time is converted to timezone: Europe/Copenhagen"""
copenhagen_timezone = pytz.timezone("Europe/Copenhagen")
time = pytz.utc.localize(datetime.utcfromtimestamp(unix_time)).astimezone(copenhagen_timezone)
if toShow:
- return time.strftime('%H:%M')
+ return time.strftime("%H:%M")
else:
- return int(time.strftime('%H%M'))
-
- @staticmethod
- def user_search(search: str) -> str:
- search = search.lower()
-
- spaceSeperator = Config.get_space_seperator()
+ return int(time.strftime("%H%M"))
- return search.replace(spaceSeperator, ' ')
-
@staticmethod
def program_time_stop(time_start: int, time_stop: int) -> int:
if time_start > time_stop:
diff --git a/tvguide/tv.py b/tvguide/tv.py
index b634a5d..e1ee1a5 100644
--- a/tvguide/tv.py
+++ b/tvguide/tv.py
@@ -1,91 +1,28 @@
-from datetime import datetime
-from .const import CHANNEL_NUMBER_INDEX
-from .format import Format, Config
+from typing import Iterator, List
import time
-
-class Channel:
- def __init__(self, channel_info: dict, verbose: bool = False) -> None:
- self.verbose = verbose
- self.id = channel_info['id']
- self.name = CHANNEL_NUMBER_INDEX[self.id]
- self.programs = []
- self.format_program_info(channel_info)
-
- def format_program_info(self, channel_info: dict) -> None:
- for program in channel_info['programs']:
- self.add_program(program)
-
- def add_program(self, program_info: dict) -> None:
- new_program = Program(program_info, self.verbose)
- self.programs.append(new_program)
-
- def print_all_programs(self) -> None:
- print(f"\n{Format.channel_name(self.name)}:")
- for program in self.programs:
- print(program.start_time_and_title)
- print()
-
- def print_searches(self, search_terms: list) -> None:
- print(f"\n{Format.channel_name(self.name)}:")
- for program in self.programs:
- for search in search_terms:
- search = Format.user_search(search)
- if search in program.title.lower():
- print(program.time_and_title)
- break
- print()
-
- def print_categories(self, categories: list) -> None:
- print(f"\n{Format.channel_name(self.name)}:")
- for program in self.programs:
- for category in categories:
- category = category.lower()
- if category in program.categories:
- print(f"{program.time_and_title} ({category.capitalize()})")
- break
- elif program.categories == [] and category in ['nyheder']:
- print(f"{program.time_and_title} ({category.capitalize()})")
- break
- print()
-
- def print_currently_running(self) -> None:
- print(f"\n{Format.channel_name(self.name)}:")
- for program in self.programs:
- if program.is_running:
- print(program.time_and_title)
- print()
-
- def print_times(self, times: list) -> None:
- print(f"\n{Format.channel_name(self.name)}:")
- for program in self.programs:
- for time in times:
- time = Format.user_time(time)
- if program.is_running_at(time):
- print(program.time_and_title)
- break
- print()
-
- def __iter__(self):
- return iter(self.programs)
+from tvguide.const import CHANNEL_NUMBER_INDEX
+from tvguide.format import Format
+from tvguide.config import ConfigManager
class Program:
def __init__(self, program_info: dict, verbose: bool = False):
self.info = program_info
self.verbose = verbose
- self.format_info()
-
- def format_info(self):
- self.time_start_unix = self.info['start']
- self.time_stop_unix = self.info['stop']
- self.time_start = Format.convert_unix_time(self.time_start_unix, toShow=False)
- self.time_stop = Format.program_time_stop(time_start=self.time_start, time_stop=Format.convert_unix_time(self.time_stop_unix, toShow=False))
- self.time_start_show = Format.convert_unix_time(self.time_start_unix, toShow=True)
- self.time_stop_show = Format.convert_unix_time(self.time_stop_unix, toShow=True)
- self.title = self.info['title']
- self.categories = [cat.lower() for cat in self.info['categories']]
- self.id = self.info['id']
+ self._format_info()
+
+ def _format_info(self):
+ self.id: str = self.info["id"]
+ self.title: str = self.info["title"]
+ self.categories: List[str] = [category.lower() for category in self.info["categories"]]
+ self.time_start_unix: int = self.info["start"]
+ self.time_stop_unix: int = self.info["stop"]
+ self.time_start: int = Format.convert_unix_time(self.time_start_unix, toShow=False)
+ temp_time_stop = Format.convert_unix_time(self.time_stop_unix, toShow=False)
+ self.time_stop: int = Format.program_time_stop(time_start=self.time_start, time_stop=temp_time_stop)
+ self.time_start_show: str = Format.convert_unix_time(self.time_start_unix, toShow=True)
+ self.time_stop_show: str = Format.convert_unix_time(self.time_stop_unix, toShow=True)
@property
def time_and_title(self) -> str:
@@ -104,7 +41,7 @@ def start_time_and_title(self) -> str:
@property
def time_and_title_and_category(self) -> str:
time_and_title = f"{self.time_start_show} - {self.time_stop_show} > {self.title}"
- justify_length = Config.get_justify_length()
+ justify_length = ConfigManager.get_justify_length()
if len(time_and_title) > justify_length:
time_and_title_cut = f"{time_and_title[:justify_length]}..."
@@ -116,7 +53,7 @@ def time_and_title_and_category(self) -> str:
@property
def start_time_and_title_and_category(self) -> str:
time_and_title = f"{self.time_start_show} > {self.title}"
- justify_length = Config.get_justify_length()
+ justify_length = ConfigManager.get_justify_length()
if len(time_and_title) > justify_length:
time_and_title_cut = f"{time_and_title[:justify_length]}..."
@@ -128,20 +65,88 @@ def start_time_and_title_and_category(self) -> str:
@property
def is_running(self) -> bool:
time_now = time.time()
+ return self.time_start_unix <= time_now < self.time_stop_unix
- if time_now == self.time_start_unix or (self.time_start_unix < time_now < self.time_stop_unix):
- return True
-
- return False
-
- def is_running_at(self, time: str):
- if self.time_start <= time < self.time_stop:
- return True
-
- return False
+ def is_running_at(self, time: int) -> bool:
+ return self.time_start <= time < self.time_stop
def __str__(self) -> str:
return self.time_and_title
def __repr__(self) -> str:
return f"Program(program_info={self.info})"
+
+
+class Channel:
+ def __init__(self, channel_info: dict, verbose: bool = False) -> None:
+ self.verbose = verbose
+ self.id: str = channel_info["id"]
+ self.name = CHANNEL_NUMBER_INDEX[self.id]
+ self.programs: List[Program] = []
+ self._parse_channel_info(channel_info)
+
+ def _parse_channel_info(self, channel_info: dict) -> None:
+ for program_dict in channel_info["programs"]:
+ program = Program(program_dict, self.verbose)
+ self.programs.append(program)
+
+ def print_all_programs(self) -> None:
+ for program in self.programs:
+ print(program.start_time_and_title)
+
+ def print_searches(self, search_terms: List[str]) -> None:
+ for search_term in search_terms:
+ programs_with_search_term = self._get_programs_with_search_term(search_term)
+ for program in programs_with_search_term:
+ print(program.time_and_title)
+
+ def print_categories(self, categories: List[str]) -> None:
+ for category in categories:
+ programs_with_category = self._get_programs_with_category(category)
+ for program in programs_with_category:
+ print(f"{program.time_and_title} ({category.capitalize()})")
+
+ def print_currently_running(self) -> None:
+ running_program = self._get_currently_running_program()
+
+ if not running_program:
+ print(
+ "*Could not find a running program"
+ " - if the time right now is after midnight before 6 am, try getting programs for yesterday*"
+ )
+ return
+
+ print(running_program.time_and_title)
+
+ def print_times(self, times: List[str]) -> None:
+ for program in self._get_programs_with_show_times(times):
+ print(program.time_and_title)
+
+ def _get_currently_running_program(self) -> Program:
+ for program in self.programs:
+ if program.is_running:
+ return program
+
+ def _get_programs_with_category(self, category: str) -> List[Program]:
+ programs_with_category: List[Program] = []
+ for program in self.programs:
+ if category.lower() in program.categories or program.categories == [] and category in ["nyheder"]:
+ programs_with_category.append(program)
+ return programs_with_category
+
+ def _get_programs_with_search_term(self, search_term: str) -> List[Program]:
+ programs_with_search_term: List[Program] = []
+ for program in self.programs:
+ if search_term.lower() in program.title.lower():
+ programs_with_search_term.append(program)
+ return programs_with_search_term
+
+ def _get_programs_with_show_times(self, show_times: List[str]) -> Iterator[Program]:
+ for program in self.programs:
+ for show_time in show_times:
+ time_int = Format.user_time(show_time)
+ if program.is_running_at(time_int):
+ yield program
+
+ def __iter__(self):
+ return iter(self.programs)
diff --git a/tvguide/tvguide.py b/tvguide/tvguide.py
new file mode 100644
index 0000000..74b0660
--- /dev/null
+++ b/tvguide/tvguide.py
@@ -0,0 +1,109 @@
+from typing import List
+import argparse
+
+from tvguide.config import ConfigManager
+from tvguide.tv import Channel
+
+
+class TvGuide:
+ def __init__(self) -> None:
+ self.relative_date: int = 0
+ self.verbose: bool = False
+ self.show_now: bool = False
+ self.show_all_channels: bool = False
+ self.show_all_programs: bool = False
+ self.search_times: bool = False
+ self.search_categories: bool = False
+ self.search_names: bool = False
+ self.input_times: List[str] = []
+ self.input_channels: List[str] = []
+ self.input_categories: List[str] = []
+ self.input_search_terms: List[str] = []
+ self.all_channels: List[Channel] = []
+
+ def parse_arguments(self, args: argparse.Namespace) -> None:
+ self.verbose = args.verbose
+ self.show_now = args.now
+ self.relative_date = args.day
+ self.show_all_programs = args.all
+
+ if args.default_channels:
+ self._change_default_channels(args.default_channels)
+
+ if args.justify_length:
+ self._change_default_justify_length(args.justify_length)
+
+ if args.search:
+ self.search_names = True
+ self.input_search_terms = args.search
+
+ if args.category:
+ self.search_categories = True
+ self.input_categories = args.category
+
+ if args.time:
+ self.search_times = True
+ self.input_times = args.time
+
+ if not args.channels:
+ self.input_channels = self.get_default_channels()
+ default_channels_string = ", ".join(self.input_channels).upper()
+ print(f"No channel(s) chosen - using default channels: {default_channels_string}")
+ elif "all" in args.channels:
+ self.show_all_channels = True
+ else:
+ self.input_channels = args.channels
+
+ def parse_api_data(self, api_data: dict) -> None:
+ for channel_dict in api_data:
+ channel = Channel(channel_dict, self.verbose)
+ self.all_channels.append(channel)
+
+ def print_programs(self) -> None:
+ channels = self.all_channels if self.show_all_channels else self.get_channels(self.input_channels)
+
+ for channel in channels:
+ print(f"\n{channel.name.upper()}")
+
+ if self.show_all_programs:
+ print("----- ALL PROGRAMS -----")
+ channel.print_all_programs()
+ print()
+
+ if self.search_categories:
+ input_categories_string = ", ".join(self.input_categories)
+ print(f"----- PROGRAMS WITH CATEGORY(s): {input_categories_string} -----")
+ channel.print_categories(self.input_categories)
+ print()
+
+ if self.search_times:
+ input_times_string = ", ".join(self.input_times)
+ print(f"----- RUNNING AT TIME(s): {input_times_string} -----")
+ channel.print_times(self.input_times)
+ print()
+
+ if self.search_names:
+ input_search_terms_string = ", ".join(self.input_search_terms)
+ print(f"----- SEARCH TERM(s): {input_search_terms_string} -----")
+ channel.print_searches(self.input_search_terms)
+ print()
+
+ if self.show_now:
+ print("----- CURRENTLY RUNNING -----")
+ channel.print_currently_running()
+ print()
+
+ def get_channels(self, channel_names: List[str]) -> List[Channel]:
+ return [channel for channel in self.all_channels if channel.name in channel_names]
+
+ def _change_default_channels(self, channels: List[str]) -> None:
+ ConfigManager.change_defaults_user_channels(channels)
+
+ def _change_default_justify_length(self, justify_length: int) -> None:
+ ConfigManager.change_justify_length(justify_length)
+
+ def get_default_channels(self) -> List[str]:
+ return ConfigManager.get_defaults_user_channels()
+
+ def get_default_justify_length(self) -> int:
+ return ConfigManager.get_justify_length()