Compare commits
2 Commits
f0130ce2fb
...
affa15e191
Author | SHA1 | Date | |
---|---|---|---|
affa15e191 | |||
6686e91236 |
5
.gitignore
vendored
5
.gitignore
vendored
@ -235,4 +235,7 @@ fabric.properties
|
|||||||
.idea/deployment.xml
|
.idea/deployment.xml
|
||||||
.idea/misc.xml
|
.idea/misc.xml
|
||||||
.idea/remote-mappings.xml
|
.idea/remote-mappings.xml
|
||||||
.idea/mvw-dl.iml
|
.idea/*.iml
|
||||||
|
|
||||||
|
# ---> Project-specific
|
||||||
|
data
|
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
@ -1,8 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
26
.idea/inspectionProfiles/Project_Default.xml
generated
26
.idea/inspectionProfiles/Project_Default.xml
generated
@ -1,26 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
|
||||||
<option name="ignoredPackages">
|
|
||||||
<value>
|
|
||||||
<list size="1">
|
|
||||||
<item index="0" class="java.lang.String" itemvalue="google" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</option>
|
|
||||||
</inspection_tool>
|
|
||||||
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
|
||||||
<option name="ignoredIdentifiers">
|
|
||||||
<list>
|
|
||||||
<option value="str.decode" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
</inspection_tool>
|
|
||||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
|
||||||
<option name="processCode" value="true" />
|
|
||||||
<option name="processLiterals" value="true" />
|
|
||||||
<option name="processComments" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
@ -7,6 +7,7 @@ state_file_retention = 50
|
|||||||
state_file_name_prefix = state-
|
state_file_name_prefix = state-
|
||||||
state_file_name_suffix = .log
|
state_file_name_suffix = .log
|
||||||
mvw_endpoint = http://localhost:8000/api/query
|
mvw_endpoint = http://localhost:8000/api/query
|
||||||
|
title_dedup_winner = first
|
||||||
|
|
||||||
[maus]
|
[maus]
|
||||||
min_duration = 1200
|
min_duration = 1200
|
||||||
@ -15,8 +16,10 @@ query = @maus-query.json
|
|||||||
# query = {"queries":[{"fields":["topic"],"query":"die sendung mit der maus"},{"fields":["channel"],"query":"ARD"}],"sortBy":"timestamp","sortOrder":"desc","future":false,"offset":0,"size":50}
|
# query = {"queries":[{"fields":["topic"],"query":"die sendung mit der maus"},{"fields":["channel"],"query":"ARD"}],"sortBy":"timestamp","sortOrder":"desc","future":false,"offset":0,"size":50}
|
||||||
# state_file_name = maus
|
# state_file_name = maus
|
||||||
# tmp_base_dir = %(tmp_base_dir)s/maus
|
# tmp_base_dir = %(tmp_base_dir)s/maus
|
||||||
|
dl_dir = ~/maus
|
||||||
|
|
||||||
[test]
|
[test]
|
||||||
min_duration = 100
|
min_duration = 100
|
||||||
max_duration = 200
|
max_duration = 200
|
||||||
query = {"queries":[{"fields":["topic"],"query":"die sendung mit der maus"},{"fields":["channel"],"query":"ARD"}],"sortBy":"timestamp","sortOrder":"desc","future":false,"offset":0,"size":50}
|
query = {"queries":[{"fields":["topic"],"query":"die sendung mit der maus"},{"fields":["channel"],"query":"ARD"}],"sortBy":"timestamp","sortOrder":"desc","future":false,"offset":0,"size":50}
|
||||||
|
dl_dir = test
|
134
main.py
134
main.py
@ -1,8 +1,20 @@
|
|||||||
import configparser
|
import configparser
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import requests
|
||||||
from rich.logging import RichHandler
|
from rich.logging import RichHandler
|
||||||
|
from rich.traceback import install
|
||||||
|
from rich.console import Console
|
||||||
|
from rich.table import Table
|
||||||
|
import typing as t
|
||||||
|
console = Console()
|
||||||
|
|
||||||
|
# We use Python 3.5+ type hints; we're working with JSON objects; we're following a 2016 suggestion from
|
||||||
|
# Python's "typing" GitHub issue tracker on how to create a "JSONType" hint since such a thing does not yet
|
||||||
|
# officially exist: https://github.com/python/typing/issues/182#issuecomment-186684288
|
||||||
|
JSONType = t.Union[str, int, float, bool, None, t.Dict[str, t.Any], t.List[t.Any]]
|
||||||
|
|
||||||
|
|
||||||
# Exit codes
|
# Exit codes
|
||||||
@ -13,9 +25,27 @@ from rich.logging import RichHandler
|
|||||||
class CONST(object):
|
class CONST(object):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
LOG_FORMAT = "%(message)s"
|
LOG_FORMAT = "%(message)s"
|
||||||
|
CFG_THIS_FILE_DIRNAME = os.path.dirname(__file__)
|
||||||
CFG_DEFAULT_FILENAME = "config.ini"
|
CFG_DEFAULT_FILENAME = "config.ini"
|
||||||
CFG_DEFAULT_ABS_PATH = os.path.join(os.getcwd(), CFG_DEFAULT_FILENAME)
|
CFG_DEFAULT_ABS_PATH = os.path.join(CFG_THIS_FILE_DIRNAME, CFG_DEFAULT_FILENAME)
|
||||||
CFG_MANDATORY = "query"
|
CFG_KNOWN_DEFAULTS = [
|
||||||
|
{"key": "self_name", "value": "mvw-dl"},
|
||||||
|
{"key": "tmp_base_dir", "value": os.path.join(CFG_THIS_FILE_DIRNAME, "data/tmp/%(self_name)s")},
|
||||||
|
{"key": "state_base_dir", "value": os.path.join(CFG_THIS_FILE_DIRNAME, "data/var/lib/%(self_name)s")},
|
||||||
|
{"key": "state_files_dir", "value": "%(state_base_dir)s/state"},
|
||||||
|
{"key": "state_file_retention", "value": "50"},
|
||||||
|
{"key": "state_file_name_prefix", "value": "state-"},
|
||||||
|
{"key": "state_file_name_suffix", "value": ".log"},
|
||||||
|
{"key": "mvw_endpoint", "value": "http://localhost:8000/api/query"},
|
||||||
|
{"key": "title_dedup_winner", "value": "first"}
|
||||||
|
]
|
||||||
|
CFG_KNOWN_SECTION = [
|
||||||
|
{"key": "min_duration", "is_mandatory": False},
|
||||||
|
{"key": "max_duration", "is_mandatory": False},
|
||||||
|
{"key": "query", "is_mandatory": True},
|
||||||
|
{"key": "dl_dir", "is_mandatory": True}
|
||||||
|
]
|
||||||
|
CFG_MANDATORY = [section_cfg["key"] for section_cfg in CFG_KNOWN_SECTION if section_cfg["is_mandatory"]]
|
||||||
|
|
||||||
|
|
||||||
CONST = CONST()
|
CONST = CONST()
|
||||||
@ -30,6 +60,7 @@ logging.basicConfig(
|
|||||||
)
|
)
|
||||||
log = logging.getLogger("rich")
|
log = logging.getLogger("rich")
|
||||||
log.setLevel(logging.DEBUG)
|
log.setLevel(logging.DEBUG)
|
||||||
|
install(show_locals=True)
|
||||||
|
|
||||||
|
|
||||||
class ConfigParser(configparser.ConfigParser):
|
class ConfigParser(configparser.ConfigParser):
|
||||||
@ -49,16 +80,7 @@ class ConfigParser(configparser.ConfigParser):
|
|||||||
|
|
||||||
|
|
||||||
ini_defaults = []
|
ini_defaults = []
|
||||||
internal_defaults = {
|
internal_defaults = {default["key"]: default["value"] for default in CONST.CFG_KNOWN_DEFAULTS}
|
||||||
"self_name": "mvw-dl",
|
|
||||||
"tmp_base_dir": "/tmp/%(self_name)s",
|
|
||||||
"state_base_dir": "/var/lib/%(self_name)s",
|
|
||||||
"state_files_dir": "%(state_base_dir)s/state",
|
|
||||||
"state_file_retention": "50",
|
|
||||||
"state_file_name_prefix": "state-",
|
|
||||||
"state_file_name_suffix": ".log",
|
|
||||||
"mvw_endpoint": "http://localhost:8000/api/query"
|
|
||||||
}
|
|
||||||
config = ConfigParser(defaults=internal_defaults)
|
config = ConfigParser(defaults=internal_defaults)
|
||||||
config.read(CONST.CFG_DEFAULT_FILENAME)
|
config.read(CONST.CFG_DEFAULT_FILENAME)
|
||||||
|
|
||||||
@ -89,7 +111,7 @@ def validate_default_section(config_obj: configparser.ConfigParser()) -> None:
|
|||||||
def config_has_valid_section(config_obj: configparser.ConfigParser()) -> bool:
|
def config_has_valid_section(config_obj: configparser.ConfigParser()) -> bool:
|
||||||
has_valid_section = False
|
has_valid_section = False
|
||||||
for config_obj_section in config_obj.sections():
|
for config_obj_section in config_obj.sections():
|
||||||
if CONST.CFG_MANDATORY in config_obj.options(config_obj_section):
|
if set(CONST.CFG_MANDATORY).issubset(config_obj.options(config_obj_section)):
|
||||||
has_valid_section = True
|
has_valid_section = True
|
||||||
break
|
break
|
||||||
return has_valid_section
|
return has_valid_section
|
||||||
@ -104,46 +126,68 @@ def is_same_as_default(config_kv_pair: dict) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def validate_config_sections(config_obj: configparser.ConfigParser()) -> None:
|
def validate_config_sections(config_obj: configparser.ConfigParser()) -> None:
|
||||||
for section in config_obj.sections():
|
for this_section in config_obj.sections():
|
||||||
log.debug(print_section_header(section))
|
log.debug(print_section_header(this_section))
|
||||||
if CONST.CFG_MANDATORY not in config_obj.options(section, no_defaults=True):
|
if not set(CONST.CFG_MANDATORY).issubset(config_obj.options(this_section, no_defaults=True)):
|
||||||
log.warning(f"Config section '[{section}]' does not have mandatory option '{CONST.CFG_MANDATORY}' set, "
|
log.warning(f"Config section '[{this_section}]' does not have all mandatory options "
|
||||||
f"skipping section ...")
|
f"{CONST.CFG_MANDATORY} set, skipping section ...")
|
||||||
config_obj.remove_section(section)
|
config_obj.remove_section(this_section)
|
||||||
else:
|
else:
|
||||||
for key in config_obj.options(section, no_defaults=True):
|
for key in config_obj.options(this_section, no_defaults=True):
|
||||||
kv_prefix = "~"
|
kv_prefix = "~"
|
||||||
if is_default(key):
|
if is_default(key):
|
||||||
kv_prefix = "+"
|
kv_prefix = "+"
|
||||||
if is_same_as_default({key: config_obj[section][key]}):
|
if is_same_as_default({key: config_obj[this_section][key]}):
|
||||||
kv_prefix = "="
|
kv_prefix = "="
|
||||||
log.debug(f"{kv_prefix} {key} = {config_obj[section][key]}")
|
log.debug(f"{kv_prefix} {key} = {config_obj[this_section][key]}")
|
||||||
|
|
||||||
|
|
||||||
validate_default_section(config)
|
def query_string_from_file(filename: str) -> str:
|
||||||
if config_has_valid_section(config):
|
with open(filename, "r") as jsonfile:
|
||||||
|
query_string = jsonfile.read()
|
||||||
|
return query_string
|
||||||
|
|
||||||
|
|
||||||
|
def get_query_payload(section_name: str, config_obj: configparser.ConfigParser()) -> JSONType:
|
||||||
|
log.debug(f"Generating HTTP POST JSON payload ...")
|
||||||
|
query = config_obj.get(section_name, "query")
|
||||||
|
if query[0] == "@":
|
||||||
|
query = query.split("@", 1)[1]
|
||||||
|
query = query_string_from_file(query)
|
||||||
|
return json.loads(query)
|
||||||
|
|
||||||
|
|
||||||
|
def get_json_response(section_name: str, config_obj: configparser.ConfigParser(), payload: JSONType) -> JSONType:
|
||||||
|
log.debug(f"Downloading JSON list of Mediathek files that match search criteria")
|
||||||
|
url = config_obj.get(section_name, "mvw_endpoint")
|
||||||
|
req_header = {"Content-Type": "text/plain", "asdasd": "aaaaaaaaaa"}
|
||||||
|
s = requests.Session()
|
||||||
|
req = requests.Request("POST", url, data=json.dumps(payload), headers=req_header)
|
||||||
|
prepped = req.prepare()
|
||||||
|
newline = "\n"
|
||||||
|
#req.method
|
||||||
|
#req.url
|
||||||
|
#for header, value in list(req.headers.items()):
|
||||||
|
# headers_table.add_row(header, value)
|
||||||
|
|
||||||
|
quit()
|
||||||
|
with s.send(prepped) as s:
|
||||||
|
pass
|
||||||
|
# log.debug(s.content)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
validate_default_section(config)
|
||||||
|
if config_has_valid_section(config):
|
||||||
validate_config_sections(config)
|
validate_config_sections(config)
|
||||||
else:
|
else:
|
||||||
log.error(f"No valid config section found. A valid config section has at least the '{CONST.CFG_MANDATORY}' "
|
log.error(f"No valid config section found. A valid config section has at least the mandatory options "
|
||||||
f"option set. Exiting 2 ...")
|
f"{CONST.CFG_MANDATORY} set. Exiting 2 ...")
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
quit()
|
log.debug(f"Iterating over config sections ...")
|
||||||
|
for section in config.sections():
|
||||||
|
log.debug(f"Processing section '[{section}]' ...")
|
||||||
|
query_payload = get_query_payload(section, config)
|
||||||
|
json_response = get_json_response(section, config, query_payload)
|
||||||
|
|
||||||
|
|
||||||
# This is a sample Python script.
|
|
||||||
|
|
||||||
# Press Umschalt+F10 to execute it or replace it with your code.
|
|
||||||
# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.
|
|
||||||
|
|
||||||
|
|
||||||
def print_hi(name):
|
|
||||||
# Use a breakpoint in the code line below to debug your script.
|
|
||||||
print(f'Hi, {name}') # Press Strg+F8 to toggle the breakpoint.
|
|
||||||
|
|
||||||
|
|
||||||
# Press the green button in the gutter to run the script.
|
|
||||||
if __name__ == '__main__':
|
|
||||||
print_hi('PyCharm')
|
|
||||||
|
|
||||||
# See PyCharm help at https://www.jetbrains.com/help/pycharm/
|
|
||||||
|
21
maus-query.json
Normal file
21
maus-query.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"queries": [
|
||||||
|
{
|
||||||
|
"fields": [
|
||||||
|
"topic"
|
||||||
|
],
|
||||||
|
"query": "die sendung mit der maus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fields": [
|
||||||
|
"channel"
|
||||||
|
],
|
||||||
|
"query": "ARD"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sortBy": "timestamp",
|
||||||
|
"sortOrder": "desc",
|
||||||
|
"future": false,
|
||||||
|
"offset": 0,
|
||||||
|
"size": 20
|
||||||
|
}
|
@ -1 +1,2 @@
|
|||||||
rich
|
rich
|
||||||
|
requests
|
@ -4,9 +4,19 @@
|
|||||||
#
|
#
|
||||||
# pip-compile
|
# pip-compile
|
||||||
#
|
#
|
||||||
|
certifi==2021.10.8
|
||||||
|
# via requests
|
||||||
|
charset-normalizer==2.0.12
|
||||||
|
# via requests
|
||||||
commonmark==0.9.1
|
commonmark==0.9.1
|
||||||
# via rich
|
# via rich
|
||||||
|
idna==3.3
|
||||||
|
# via requests
|
||||||
pygments==2.11.2
|
pygments==2.11.2
|
||||||
# via rich
|
# via rich
|
||||||
|
requests==2.27.1
|
||||||
|
# via -r requirements.in
|
||||||
rich==12.0.0
|
rich==12.0.0
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
|
urllib3==1.26.8
|
||||||
|
# via requests
|
||||||
|
Loading…
x
Reference in New Issue
Block a user