...
 
Commits (3)
......@@ -5,7 +5,7 @@ stages:
- release
default:
image: namboy94/ci-docker-environment:0.6.0
image: namboy94/ci-docker-environment:0.8.0
before_script:
- echo "$SERVER_ACCESS_KEY" > ~/.ssh/id_rsa
- chmod 0600 ~/.ssh/id_rsa
......@@ -25,7 +25,13 @@ stylecheck:
stage: test
tags: [docker]
script:
- python-codestyle-check
- python-codestyle-check --exclude toktokkie/gui/pyuic
type_check:
stage: test
tags: [docker]
script:
- python-static-type-check
unittest:
stage: test
......
......@@ -43,7 +43,7 @@ class BookSeriesChecker(Checker):
Checks if the anilist user is up-to-date with all available volumes
:return: The check result
"""
metadata = self.metadata # type: BookSeries
metadata = self.metadata # type: BookSeries # type: ignore
manga_list = self.config["anilist_manga_list"]
anilist_id = metadata.ids.get(IdType.ANILIST, [None])[0]
......
......@@ -69,7 +69,7 @@ class MangaChecker(Checker):
:return: The result of the check
"""
# noinspection PyTypeChecker
metadata = self.metadata # type: Manga
metadata = self.metadata # type: Manga # type: ignore
anilist_entries = self.config["anilist_manga_list"]
local_chaptercount = len(os.listdir(metadata.main_path))
......
......@@ -19,7 +19,7 @@ along with toktokkie. If not, see <http://www.gnu.org/licenses/>.
import os
from tvdb_api import tvdb_shownotfound
from typing import Dict, Optional
from typing import Dict, Optional, List
from datetime import datetime
from colorama import Fore, Style
from toktokkie.check.Checker import Checker
......@@ -68,7 +68,7 @@ class TvSeriesChecker(Checker):
:return: The result of the check
"""
valid = True
metadata = self.metadata # type: TvSeries
metadata = self.metadata # type: TvSeries # type: ignore
for season_name in os.listdir(metadata.directory_path):
season_path = os.path.join(metadata.directory_path, season_name)
......@@ -92,7 +92,7 @@ class TvSeriesChecker(Checker):
:return: The result of the check
"""
valid = True
metadata = self.metadata # type: TvSeries
metadata = self.metadata # type: TvSeries # type: ignore
ids = [metadata.tvdb_id]
for season in metadata.seasons:
......@@ -115,7 +115,7 @@ class TvSeriesChecker(Checker):
:return: The result of the check
"""
valid = True
metadata = self.metadata # type: TvSeries
metadata = self.metadata # type: TvSeries # type: ignore
ignores = self._generate_ignores_map()
tvdb_data = self.tvdb[int(metadata.tvdb_id)]
......@@ -132,8 +132,8 @@ class TvSeriesChecker(Checker):
elif episode_number in ignores.get(season_number, []):
episode_amount -= 1
existing = metadata.get_episode_files()
existing = existing[metadata.tvdb_id].get(season_number, [])
_existing = metadata.get_episode_files()
existing = _existing[metadata.tvdb_id].get(season_number, [])
if not len(existing) == episode_amount:
msg = "Mismatch in season {}; Should:{}; Is:{}".format(
......@@ -150,7 +150,7 @@ class TvSeriesChecker(Checker):
:return: The result of the check
"""
valid = True
metadata = self.metadata # type: TvSeries
metadata = self.metadata # type: TvSeries # type: ignore
ignores = self._generate_ignores_map()
tvdb_data = self.tvdb[int(metadata.tvdb_id)]
......@@ -196,7 +196,7 @@ class TvSeriesChecker(Checker):
:return: The result of the check
"""
valid = True
metadata = self.metadata # type: TvSeries
metadata = self.metadata # type: TvSeries # type: ignore
episode_files = metadata.get_episode_files()
for season in metadata.seasons:
......@@ -243,7 +243,7 @@ class TvSeriesChecker(Checker):
:return: The check result
"""
metadata = self.metadata # type: TvSeries
metadata = self.metadata # type: TvSeries # type: ignore
api = self.config["anilist_api"] # type: AnilistApi
user_list = self.config["anilist_anime_list"]
completed_ids = list(
......@@ -283,7 +283,7 @@ class TvSeriesChecker(Checker):
if anilist_id is not None:
anilist_ids.append(str(anilist_id))
resp = print(
resp = input(
"{}Set anilist IDs to {}? (y|n){}".format(
Fore.LIGHTGREEN_EX, anilist_ids, Style.RESET_ALL
)
......@@ -311,15 +311,14 @@ class TvSeriesChecker(Checker):
# check if amount of episode files is correct
# Note: Make sure to think of multi-episodes etc
def _generate_ignores_map(self) -> Dict[int, int]:
def _generate_ignores_map(self) -> Dict[int, List[int]]:
"""
Generates a dictionary mapping the excluded episode number to their
respective episodes.
:return: The generated dictionary: {season: [episodes]}
"""
metadata = self.metadata # type: TvSeries
ignores = {}
metadata = self.metadata # type: TvSeries # type: ignore
ignores = {} # type: Dict[int, List[int]]
excluded = metadata.excludes.get(IdType.TVDB, {})
multis = metadata.multi_episodes.get(IdType.TVDB, {})
......@@ -358,7 +357,7 @@ class TvSeriesChecker(Checker):
:param series_name_override: Overrides the series name
:return: The generated name
"""
metadata = self.metadata # type: TvSeries
metadata = self.metadata # type: TvSeries # type: ignore
multis = metadata.multi_episodes.get(IdType.TVDB, {})
series_name = metadata.name
......
......@@ -66,7 +66,7 @@ class VisualNovelChecker(Checker):
file.
:return: The check result
"""
metadata = self.metadata # type: VisualNovel
metadata = self.metadata # type: VisualNovel # type: ignore
valid = True
if metadata.has_op and metadata.ops is None:
......
......@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
along with toktokkie. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
from typing import Optional
from subprocess import Popen
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QMainWindow, QWidget
......@@ -37,7 +38,7 @@ class BookWidget(QWidget, Ui_BookWidget):
"""
super().__init__(parent)
self.setupUi(self)
self.metadata = None # type: Book
self.metadata = None # type: Optional[Book]
self.initialize_buttons()
def initialize_buttons(self):
......
......@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
along with toktokkie. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
from typing import Optional
from subprocess import Popen
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QMainWindow, QWidget
......@@ -37,7 +38,7 @@ class MovieWidget(QWidget, Ui_MovieWidget):
"""
super().__init__(parent)
self.setupUi(self)
self.metadata = None # type: Movie
self.metadata = None # type: Optional[Movie]
self.initialize_buttons()
def initialize_buttons(self):
......
......@@ -21,6 +21,7 @@ import os
import tvdb_api
import requests
import webbrowser
from typing import Optional
from subprocess import Popen
from threading import Thread
from PyQt5.QtGui import QPixmap
......@@ -43,8 +44,8 @@ class TvSeasonWidget(QWidget, Ui_TvSeasonWidget):
"""
super().__init__(parent)
self.setupUi(self)
self.metadata = None # type: TvSeries
self.season = None # type: TvSeason
self.metadata = None # type: Optional[TvSeries]
self.season = None # type: Optional[TvSeason]
self.initialize_buttons()
def initialize_buttons(self):
......
......@@ -19,6 +19,7 @@ LICENSE"""
import tvdb_api
import webbrowser
from typing import Optional
from subprocess import Popen
from threading import Thread
from PyQt5.QtGui import QPixmap
......@@ -40,7 +41,7 @@ class TvSeriesWidget(QWidget, Ui_TvSeriesWidget):
"""
super().__init__(parent)
self.setupUi(self)
self.metadata = None # type: TvSeries
self.metadata = None # type: Optional[TvSeries]
self.initialize_buttons()
def initialize_buttons(self):
......
......@@ -20,12 +20,13 @@ LICENSE"""
from typing import Type
from toktokkie.iconizing.procedures.Procedure import Procedure
from toktokkie.iconizing.procedures.GnomeProcedure import GnomeProcedure
from toktokkie.iconizing.procedures.NoopProcedure import NoopProcedure
from toktokkie.iconizing.Iconizer import Iconizer
procedures = [GnomeProcedure]
def default_procedure() -> Type[Procedure] or None:
def default_procedure() -> Type[Procedure]:
"""
Checks all available procedures for eligibility
:return: The eligible procedure or None if none were found
......@@ -33,4 +34,4 @@ def default_procedure() -> Type[Procedure] or None:
for procedure in procedures:
if procedure.is_applicable():
return procedure
return None
return NoopProcedure
......@@ -79,3 +79,6 @@ class GnomeProcedure(Procedure):
and gvfs_installed and gvfs_check
except KeyError: # pragma: no cover
return False
else:
return False
......@@ -17,32 +17,31 @@ You should have received a copy of the GNU General Public License
along with toktokkie. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
from toktokkie.iconizing.procedures.Procedure import Procedure
class PromptType:
class NoopProcedure(Procedure):
"""
Class that allows the automatic conversion of user-provided strings to
values
Iconizing Procedure that does nothing. Used as a fallback when no
other procedures are available
"""
def __init__(self, value: str):
"""
Initializes the prompt type
:param value: The string value to parse
"""
self._value = value
# noinspection PyStatementEffect
self.value
@property
def value(self):
@classmethod
def iconize(cls, directory: str, icon_path_no_ext):
"""
Converts the string value into its actual value
:return: The generated value
Doesn't do anything
:param directory: The directory to iconize
:param icon_path_no_ext: The icon file without a file extension.
.png will be appended
:return: None
"""
raise NotImplementedError()
pass
def __str__(self) -> str:
@classmethod
def is_applicable(cls) -> bool:
"""
:return: A string representation of this object
Checks if this procedure is applicable to the current system.
NOOP Procedure is always applicable
:return: True if applicable, else False
"""
return str(self.value)
return True
......@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with toktokkie. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
import os
from typing import Dict, Any
from toktokkie.metadata.Metadata import Metadata
from toktokkie.metadata.components.enums import MediaType
......@@ -35,16 +35,22 @@ class Book(Metadata):
return MediaType.BOOK
@classmethod
def prompt(cls, directory_path: str) -> Metadata:
def _prompt(cls, directory_path: str, json_data: Dict[str, Any]) \
-> Dict[str, Any]:
"""
Generates a new Metadata object using prompts for a directory
Prompts the user for metadata-type-specific information
Should be extended by child classes
:param directory_path: The path to the directory for which to generate
the metadata object
:return: The generated metadata object
the metadata
:param json_data: Previously generated JSON data
:return: The generated metadata JSON data
"""
print("Generating metadata for {}:"
.format(os.path.basename(directory_path)))
return cls(directory_path, {
"ids": cls.prompt_for_ids(),
"type": cls.media_type().value
})
return {}
def _validate_json(self):
"""
Validates the JSON data to make sure everything has valid values
:raises InvalidMetadataException: If any errors were encountered
:return: None
"""
pass
......@@ -18,7 +18,7 @@ along with toktokkie. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
import os
from typing import List
from typing import List, Dict, Any
from puffotter.os import listdir
from toktokkie.metadata.Book import Book
from toktokkie.metadata.components.BookVolume import BookVolume
......@@ -39,16 +39,16 @@ class BookSeries(Book):
return MediaType.BOOK_SERIES
@classmethod
def prompt(cls, directory_path: str) -> Book:
def _prompt(cls, directory_path: str, json_data: Dict[str, Any]) \
-> Dict[str, Any]:
"""
Generates a new Metadata object using prompts for a directory
Prompts the user for metadata-type-specific information
Should be extended by child classes
:param directory_path: The path to the directory for which to generate
the metadata object
:return: The generated metadata object
the metadata
:param json_data: Previously generated JSON data
:return: The generated metadata JSON data
"""
print("Generating metadata for {}:"
.format(os.path.basename(directory_path)))
series_ids = cls.prompt_for_ids()
series = cls(directory_path, {
"volumes": [],
......@@ -80,9 +80,9 @@ class BookSeries(Book):
})
series.volumes = volumes
return series
return series.json
@property
@property # type: ignore
@json_parameter
def volumes(self) -> List[BookVolume]:
"""
......@@ -109,3 +109,11 @@ class BookSeries(Book):
self.json["volumes"] = {}
for i, volume in enumerate(volumes):
self.json["volumes"][i] = volume.json
def _validate_json(self):
"""
Validates the JSON data to make sure everything has valid values
:raises InvalidMetadataException: If any errors were encountered
:return: None
"""
raise NotImplementedError()
......@@ -18,11 +18,11 @@ along with toktokkie. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
import os
from typing import List
from typing import List, Dict, Any
from toktokkie.metadata.Metadata import Metadata
from toktokkie.metadata.components.enums import MediaType
from toktokkie.metadata.prompt.CommaList import CommaList
from toktokkie.metadata.helper.wrappers import json_parameter
from puffotter.prompt import prompt_comma_list
class Manga(Metadata):
......@@ -38,15 +38,16 @@ class Manga(Metadata):
return MediaType.MANGA
@classmethod
def prompt(cls, directory_path: str) -> Metadata:
def _prompt(cls, directory_path: str, json_data: Dict[str, Any]) \
-> Dict[str, Any]:
"""
Generates a new Metadata object using prompts for a directory
Prompts the user for metadata-type-specific information
Should be extended by child classes
:param directory_path: The path to the directory for which to generate
the metadata object
:return: The generated metadata object
the metadata
:param json_data: Previously generated JSON data
:return: The generated metadata JSON data
"""
print("Generating metadata for {}:"
.format(os.path.basename(directory_path)))
series = cls(directory_path, {
"ids": cls.prompt_for_ids(),
"type": cls.media_type().value,
......@@ -57,12 +58,11 @@ class Manga(Metadata):
print("Please enter identifiers for special chapters:")
for _file in sorted(os.listdir(series.special_path)):
print(_file)
series.special_chapters = cls.input(
"Special Chapters", CommaList(""), CommaList
).value
return series
series.special_chapters = prompt_comma_list("Special Chapters")
@property
return series.json
@property # type: ignore
def main_path(self) -> str:
"""
The path to the main manga directory
......@@ -70,7 +70,7 @@ class Manga(Metadata):
"""
return os.path.join(self.directory_path, "Main")
@property
@property # type: ignore
def special_path(self) -> str:
"""
The path to the special manga directory
......@@ -78,7 +78,7 @@ class Manga(Metadata):
"""
return os.path.join(self.directory_path, "Special")
@property
@property # type: ignore
@json_parameter
def special_chapters(self) -> List[str]:
"""
......@@ -96,3 +96,11 @@ class Manga(Metadata):
max_len = len(max(special_chapters, key=lambda x: len(x)))
special_chapters.sort(key=lambda x: x.zfill(max_len))
self.json["special_chapters"] = special_chapters
def _validate_json(self):
"""
Validates the JSON data to make sure everything has valid values
:raises InvalidMetadataException: If any errors were encountered
:return: None
"""
raise NotImplementedError()
This diff is collapsed.
......@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with toktokkie. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
import os
from typing import Dict, Any
from toktokkie.metadata.Metadata import Metadata
from toktokkie.metadata.components.enums import MediaType
......@@ -35,16 +35,22 @@ class Movie(Metadata):
return MediaType.MOVIE
@classmethod
def prompt(cls, directory_path: str) -> Metadata:
def _prompt(cls, directory_path: str, json_data: Dict[str, Any]) \
-> Dict[str, Any]:
"""
Generates a new Metadata object using prompts for a directory
Prompts the user for metadata-type-specific information
Should be extended by child classes
:param directory_path: The path to the directory for which to generate
the metadata object
:return: The generated metadata object
the metadata
:param json_data: Previously generated JSON data
:return: The generated metadata JSON data
"""
print("Generating metadata for {}:"
.format(os.path.basename(directory_path)))
return cls(directory_path, {
"ids": cls.prompt_for_ids(),
"type": cls.media_type().value
})
return {}
def _validate_json(self):
"""
Validates the JSON data to make sure everything has valid values
:raises InvalidMetadataException: If any errors were encountered
:return: None
"""
pass
......@@ -19,7 +19,7 @@ LICENSE"""
import os
import tvdb_api
from typing import List, Dict
from typing import List, Dict, Any, Optional
from toktokkie.metadata.Metadata import Metadata
from toktokkie.metadata.helper.wrappers import json_parameter
from toktokkie.metadata.components.TvSeason import TvSeason
......@@ -42,21 +42,24 @@ class TvSeries(Metadata):
return MediaType.TV_SERIES
@classmethod
def prompt(cls, directory_path: str) -> Metadata:
def _prompt(cls, directory_path: str, json_data: Dict[str, Any]) \
-> Dict[str, Any]:
"""
Generates a new Metadata object using prompts for a directory
Prompts the user for metadata-type-specific information
Should be extended by child classes
:param directory_path: The path to the directory for which to generate
the metadata object
:return: The generated metadata object
the metadata
:param json_data: Previously generated JSON data
:return: The generated metadata JSON data
"""
name = os.path.basename(directory_path)
print("Generating metadata for {}:".format(name))
probable_defaults = None # type: Optional[Dict[str, List[str]]]
try:
probable_tvdb_id = str(tvdb_api.Tvdb()[name].data["id"])
probable_defaults = {IdType.TVDB.value: [probable_tvdb_id]}
except (tvdb_api.tvdb_shownotfound, TypeError):
probable_defaults = None
pass
series_ids = cls.prompt_for_ids(
defaults=probable_defaults,
......@@ -89,9 +92,9 @@ class TvSeries(Metadata):
}))
series.seasons = seasons
return series
return series.json
@property
@property # type: ignore
@json_parameter
def tvdb_id(self) -> str:
"""
......@@ -99,7 +102,7 @@ class TvSeries(Metadata):
"""
return self.ids[IdType.TVDB][0]
@property
@property # type: ignore
@json_parameter
def seasons(self) -> List[TvSeason]:
"""
......@@ -137,7 +140,7 @@ class TvSeries(Metadata):
return season
raise KeyError(season_name)
@property
@property # type: ignore
@json_parameter
def excludes(self) -> Dict[IdType, Dict[int, List[int]]]:
"""
......@@ -145,7 +148,7 @@ class TvSeries(Metadata):
:return A dictionary mapping episode info to seasons and id types
Form: {idtype: {season: [ep1, ep2]}}
"""
generated = {}
generated = {} # type: Dict[IdType, Dict[int, List[int]]]
for _id_type in self.json.get("excludes", {}):
......@@ -167,7 +170,7 @@ class TvSeries(Metadata):
return generated
@property
@property # type: ignore
@json_parameter
def season_start_overrides(self) -> Dict[IdType, Dict[int, int]]:
"""
......@@ -175,7 +178,7 @@ class TvSeries(Metadata):
point to ID types
Form: {idtype: {season: episode}}
"""
generated = {}
generated = {} # type: Dict[IdType, Dict[int, int]]
for _id_type, overrides in \
self.json.get("season_start_overrides", {}).items():
......@@ -191,14 +194,14 @@ class TvSeries(Metadata):
return generated
@property
@property # type: ignore
@json_parameter
def multi_episodes(self) -> Dict[IdType, Dict[int, Dict[int, int]]]:
"""
:return: A dictionary mapping lists of multi-episodes to id types
Form: {idtype: {season: {start: end}}}
"""
generated = {}
generated = {} # type: Dict[IdType, Dict[int, Dict[int, int]]]
for _id_type in self.json.get("multi_episodes", {}):
......@@ -268,13 +271,12 @@ class TvSeries(Metadata):
"episode": episode
})
def validate_json(self):
def _validate_json(self):
"""
Validates the JSON data to make sure everything has valid values
:raises InvalidMetadataException: If any errors were encountered
:return: None
"""
super().validate_json()
self._assert_true("seasons" in self.json)
self._assert_true(len(self.seasons) == len(self.json["seasons"]))
self._assert_true(
......@@ -304,7 +306,7 @@ class TvSeries(Metadata):
:return: The generated dictionary. It will have the following form:
{tvdb_id: {season_number: [episode_files]}}
"""
content_info = {}
content_info = {} # type: Dict[str, Dict[int, List[str]]]
for season_name in os.listdir(self.directory_path):
......
......@@ -18,11 +18,11 @@ along with toktokkie. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
import os
from typing import Optional, List, Dict
from typing import Optional, List, Dict, Any
from puffotter.os import listdir
from toktokkie.metadata.Metadata import Metadata
from toktokkie.metadata.helper.wrappers import json_parameter
from toktokkie.metadata.components.enums import IdType, MediaType
from toktokkie.metadata.components.enums import MediaType
class VisualNovel(Metadata):
......@@ -38,21 +38,19 @@ class VisualNovel(Metadata):
return MediaType.VISUAL_NOVEL
@classmethod
def prompt(cls, directory_path: str) -> Metadata:
def _prompt(cls, directory_path: str, json_data: Dict[str, Any]) \
-> Dict[str, Any]:
"""
Generates a new Metadata object using prompts for a directory
Prompts the user for metadata-type-specific information
Should be extended by child classes
:param directory_path: The path to the directory for which to generate
the metadata object
:return: The generated metadata object
the metadata
:param json_data: Previously generated JSON data
:return: The generated metadata JSON data
"""
print("Generating metadata for {}:"
.format(os.path.basename(directory_path)))
return cls(directory_path, {
"ids": cls.prompt_for_ids(required=[IdType.VNDB]),
"type": cls.media_type().value
})
return {}
@property
@property # type: ignore
@json_parameter
def has_ed(self) -> bool:
"""
......@@ -69,7 +67,7 @@ class VisualNovel(Metadata):
"""
self.json["has_ed"] = has_ed
@property
@property # type: ignore
@json_parameter
def has_op(self) -> bool:
"""
......@@ -86,7 +84,7 @@ class VisualNovel(Metadata):
"""
self.json["has_op"] = has_op
@property
@property # type: ignore
@json_parameter
def has_cgs(self) -> bool:
"""
......@@ -103,7 +101,7 @@ class VisualNovel(Metadata):
"""
self.json["has_cgs"] = has_cgs
@property
@property # type: ignore
@json_parameter
def has_ost(self) -> bool:
"""
......@@ -187,3 +185,11 @@ class VisualNovel(Metadata):
return None
else:
return files
def _validate_json(self):
"""
Validates the JSON data to make sure everything has valid values
:raises InvalidMetadataException: If any errors were encountered
:return: None
"""
pass
......@@ -65,4 +65,4 @@ class TvSeason(MetadataPart):
"""
:return: Whether or not this season is a spinoff
"""
return self.parent.tvdb_id != self.tvdb_id
return self.parent.tvdb_id != self.tvdb_id # type: ignore
......@@ -19,6 +19,7 @@ LICENSE"""
import os
import json
from typing import Union, Type
from toktokkie.metadata.Metadata import Metadata
from toktokkie.metadata.Book import Book
from toktokkie.metadata.BookSeries import BookSeries
......@@ -47,7 +48,8 @@ def get_metadata(directory: str) -> Metadata:
raise InvalidMetadata()
def create_metadata(directory: str, media_type: str or MediaType) -> Metadata:
def create_metadata(directory: str, media_type: Union[str, MediaType]) \
-> Metadata:
"""
Generates a new metadata object using user prompts
:param directory: The directory for which to generate the metadata
......@@ -60,7 +62,7 @@ def create_metadata(directory: str, media_type: str or MediaType) -> Metadata:
return metadata
def get_metadata_class(media_type: str or MediaType) -> type(Metadata):
def get_metadata_class(media_type: Union[str, MediaType]) -> Type[Metadata]:
"""
Retrieves the metadata class for a given media type
:param media_type: The media type for which to get the metadata class
......@@ -69,7 +71,7 @@ def get_metadata_class(media_type: str or MediaType) -> type(Metadata):
if type(media_type) == str:
media_type = MediaType(media_type)
return {
return { # type: ignore
Book.media_type(): Book,
BookSeries.media_type(): BookSeries,
TvSeries.media_type(): TvSeries,
......
"""LICENSE
Copyright 2015 Hermann Krumrey <hermann@krumreyh.com>
This file is part of toktokkie.
toktokkie is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
toktokkie is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with toktokkie. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
from typing import List, Any
from toktokkie.metadata.prompt.PromptType import PromptType
class CommaList(PromptType):
"""
Class that allows the automatic conversion of user-provided strings to
values
"""
primitive = str
"""
The primitive type to contain within the comma list
"""
@property
def value(self) -> List[Any]:
"""
Converts the string value into its actual value
:return: The generated value
"""
values = []
for split in self._value.split(","):
values.append(self.primitive(split.strip()))
return values
class IntCommaList(CommaList):
"""
A comma list using integer values
"""
primitive = int
"""
The primitive type to contain within the comma list
"""
"""LICENSE
Copyright 2015 Hermann Krumrey <hermann@krumreyh.com>
This file is part of toktokkie.
toktokkie is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
toktokkie is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with toktokkie. If not, see <http://www.gnu.org/licenses/>.
LICENSE"""
......@@ -44,6 +44,7 @@ class Renamer:
self.path = metadata.directory_path
self.metadata = metadata
self.operations = [] # type: List[RenameOperation]
if self.metadata.media_type() == MediaType.BOOK:
self.operations = self._generate_book_operations()
elif self.metadata.media_type() == MediaType.BOOK_SERIES:
......@@ -54,9 +55,6 @@ class Renamer:
self.operations = self._generate_tv_series_operations()
elif self.metadata.media_type() == MediaType.MANGA:
self.operations = self._generate_manga_operations()
# Visual Novels don't get renamed!
else: # pragma: no cover
self.operations = [] # type: List[RenameOperation]
def get_active_operations(self) -> List[RenameOperation]:
"""
......@@ -130,7 +128,7 @@ class Renamer:
:return: The list of rename operations
"""
# noinspection PyTypeChecker
metadata = self.metadata # type: Manga
metadata = self.metadata # type: Manga # type: ignore
main_content = listdir(metadata.main_path, no_dirs=True)
max_chapter_length = len(str(len(main_content)))
......@@ -230,7 +228,7 @@ class Renamer:
operations = []
# noinspection PyTypeChecker
tv_series_metadata = self.metadata # type: TvSeries
tv_series_metadata = self.metadata # type: TvSeries # type: ignore
excluded = tv_series_metadata.excludes.get(IdType.TVDB, {})
multis = tv_series_metadata.multi_episodes.get(IdType.TVDB, {})
......@@ -361,8 +359,8 @@ class Renamer:
try:
tvdb = tvdb_api.Tvdb()
tvdb_id = int(tvdb_id)
return tvdb[tvdb_id][season_number][episode_number]["episodeName"]
info = tvdb[int(tvdb_id)]
return info[season_number][episode_number]["episodeName"]
except (tvdb_episodenotfound, tvdb_seasonnotfound,
tvdb_shownotfound, ConnectionError, KeyError) as e:
......
......@@ -24,7 +24,8 @@ from toktokkie import Directory
from puffotter.graphql import GraphQlClient
from puffotter.os import makedirs
from subprocess import Popen
from toktokkie.metadata.Manga import Manga, MangaIdType
from toktokkie.metadata.Manga import Manga
from toktokkie.metadata.components.enums import IdType
from toktokkie.exceptions import MissingMetadata, InvalidMetadata
from manga_dl.scrapers.mangadex import MangaDexScraper
from toktokkie.scripts.Command import Command
......@@ -89,7 +90,7 @@ class MangaCreateCommand(Command):
try:
directory = Directory(title)
mangadex_id = \
directory.metadata.ids.get(MangaIdType.MANGADEX, [None])[0]
directory.metadata.ids.get(IdType.MANGADEX, [None])[0]
except (MissingMetadata, InvalidMetadata):
......
......@@ -193,7 +193,7 @@ class MangaUpdateCommand(Command):
total_chapters = len(main_chapters) + current_latest
downloaded = []
downloaded = [] # type: List[str]
for c in main_chapters:
if current_latest + 1 != c.macro_chapter:
pprint("Missing chapter {}, expected {}".format(
......
......@@ -19,6 +19,7 @@ LICENSE"""
import argparse
from toktokkie.scripts.Command import Command
from toktokkie.metadata.components.enums import IdType
class MetadataSetCommand(Command):
......@@ -42,12 +43,14 @@ class MetadataSetCommand(Command):
"""
cls.add_directories_arg(parser)
id_types = [x.value for x in IdType]
subparser = parser.add_subparsers(required=True, dest="mode")
multi_episode_parser = subparser.add_parser(
"multi-episode", help="Add a multi-episode to a TV Series"
)
multi_episode_parser.add_argument("id_type", choices=tv_id_types,
multi_episode_parser.add_argument("id_type", choices=id_types,
help="The ID type")
multi_episode_parser.add_argument("season", type=int,
help="The season of the episode")
......@@ -59,19 +62,16 @@ class MetadataSetCommand(Command):
exclude_parser = subparser.add_parser(
"exclude", help="Add an excluded episode to a TV Series"
)
exclude_parser.add_argument("id_type", choices=tv_id_types,
exclude_parser.add_argument("id_type", choices=id_types,
help="The ID type")
exclude_parser.add_argument("season", type=int,
help="The season of the episode")
exclude_parser.add_argument("episode", type=int,
help="The episode to exclude")
def execute(self):
"""
Executes the commands
:return: None
"""
for directory in self.load_directories(self.args.directories):
print(directory.metadata)
pass
......@@ -31,9 +31,7 @@ from toktokkie.scripts.XdccUpdateCommand import XdccUpdateCommand
try:
from toktokkie.scripts.GuiCommand import GuiCommand
except ImportError as e:
raise e
print("W")
GuiCommand = None
GuiCommand = None # type: ignore
toktokkie_commands = [
PrintCommand,
......
......@@ -39,13 +39,6 @@ class _TestMetadata(_TestFramework):
"""
raise NotImplementedError()
def test_invalid_metadata(self):
"""
Tests if invalid metadata is identified correctly
:return: None
"""
raise NotImplementedError()
def test_validation(self):
"""
Tests if the validation of metadata works correctly
......
......@@ -20,6 +20,8 @@ LICENSE"""
import os
import tvdb_api
from toktokkie.Directory import Directory
from toktokkie.exceptions import InvalidMetadata
from toktokkie.metadata.TvSeries import TvSeries
from toktokkie.test.metadata.TestMetadata import _TestMetadata
......@@ -82,19 +84,18 @@ class TestTv(_TestMetadata):
"""
pass
def test_invalid_metadata(self):
"""
Tests if invalid metadata is identified correctly
:return: None
"""
pass
def test_validation(self):
"""
Tests if the validation of metadata works correctly
:return: None
"""
pass
path = self.get("Suzumiya Haruhi no Yuuutsu")
try:
TvSeries(path, {})
self.fail()
except InvalidMetadata:
pass
def test_checking(self):
"""
......
......@@ -20,7 +20,7 @@ LICENSE"""
import os
import re
import json
from typing import Dict, Optional, Set, Any
from typing import Dict, Optional, Set, Any, Union
from xdcc_dl.xdcc import download_packs
from xdcc_dl.pack_search.SearchEngine import SearchEngineType, SearchEngine
from toktokkie.renaming import Renamer, RenameOperation
......@@ -215,7 +215,7 @@ class XDCCUpdater:
default: Optional[str] = None,
choices: Optional[Set[str]] = None,
is_int: bool = False
) -> str or int:
) -> Union[str, int]:
"""
Creates a user prompt
:param prompt_text: The text to show the user
......@@ -247,11 +247,11 @@ class XDCCUpdater:
if is_int:
try:
resp = int(resp)
return int(resp)
except ValueError:
print("Not an Integer")
return resp
else:
return resp
@classmethod
def prompt(cls, metadata: TvSeries):
......