Repository: Flexget/Flexget

Issue:
Unhandled error in plugin series: expected string or bytes-like object
### Expected behaviour:
Plugin series should properly parse qualities.

### Actual behaviour:
Crash at each run

### Steps to reproduce:
- Step 1: ...

#### Config:
https://pastebin.cedricgirard.fr/view/15f3af31
  
#### Log:
https://pastebin.cedricgirard.fr/view/f8c4b3c9

### Additional information:

- FlexGet version: 2.11.24
- Python version: 3.6.4
- Installation method: Distrib package built from git
- Using daemon (yes/no): yes
- OS and version: ArchLinux (Rolling, Up to Date)
Unhandled error in plugin series: expected string or bytes-like object
### Expected behaviour:
Plugin series should properly parse qualities.

### Actual behaviour:
Crash at each run

### Steps to reproduce:
- Step 1: ...

#### Config:
https://pastebin.cedricgirard.fr/view/15f3af31
  
#### Log:
https://pastebin.cedricgirard.fr/view/f8c4b3c9

### Additional information:

- FlexGet version: 2.11.24
- Python version: 3.6.4
- Installation method: Distrib package built from git
- Using daemon (yes/no): yes
- OS and version: ArchLinux (Rolling, Up to Date)


Discussion:
It seems I am able to avoid the crash with testing if text is a string:

```diff
--- /home/cedric/ArchLinux/pkgbuild/flexget-git/src/Flexget/flexget/utils/qualities.py  2018-01-12 13:35:19.877767841 +0100
+++ /usr/lib/python3.6/site-packages/flexget/utils/qualities.py 2018-01-15 18:08:59.442420460 +0100
@@ -41,14 +41,16 @@
         :param string text: data te be tested against
         :returns: tuple (matches, remaining text without quality data)
         """
-
-        match = self.regexp.search(text)
-        if not match:
+        if not isinstance(text,str):
             return False, ""
         else:
-            # remove matching part from the text
-            text = text[:match.start()] + text[match.end():]
-        return True, text
+            match = self.regexp.search(text)
+            if not match:
+                return False, ""
+            else:
+                # remove matching part from the text
+                text = text[:match.start()] + text[match.end():]
+            return True, text
```
This is related to sqlalchemy >= 1.2. Downgrading sqlalchemy python package fix this (without my previous patch).
Interesting, thanks for updating. 
Failure incurred with installed: community/python-sqlalchemy 1.2.1-1 and flexget version 2.10.48

Fixed by manuall installing SQLAlchemy 1.1.14 (https://pypi.python.org/pypi/SQLAlchemy/1.1.14) like this..

pip2.7 install https://pypi.python.org/packages/a3/d1/eacdedd964ad43fbc4cc7110dac33fbf1dbd7bf50dedb4dc1799c42972c1/SQLAlchemy-1.1.14.tar.gz
Reproduced and started investigating.

Reproduced with this:
```yaml
  series_bug:
    mock:
    - title: Foo.S01E01.1080P.HDTV-Flexget
    metainfo_series: yes
    series:
    - Foo:
        quality: 1080p
```
It seems I am able to avoid the crash with testing if text is a string:

```diff
--- /home/cedric/ArchLinux/pkgbuild/flexget-git/src/Flexget/flexget/utils/qualities.py  2018-01-12 13:35:19.877767841 +0100
+++ /usr/lib/python3.6/site-packages/flexget/utils/qualities.py 2018-01-15 18:08:59.442420460 +0100
@@ -41,14 +41,16 @@
         :param string text: data te be tested against
         :returns: tuple (matches, remaining text without quality data)
         """
-
-        match = self.regexp.search(text)
-        if not match:
+        if not isinstance(text,str):
             return False, ""
         else:
-            # remove matching part from the text
-            text = text[:match.start()] + text[match.end():]
-        return True, text
+            match = self.regexp.search(text)
+            if not match:
+                return False, ""
+            else:
+                # remove matching part from the text
+                text = text[:match.start()] + text[match.end():]
+            return True, text
```
This is related to sqlalchemy >= 1.2. Downgrading sqlalchemy python package fix this (without my previous patch).
Interesting, thanks for updating. 
Failure incurred with installed: community/python-sqlalchemy 1.2.1-1 and flexget version 2.10.48

Fixed by manuall installing SQLAlchemy 1.1.14 (https://pypi.python.org/pypi/SQLAlchemy/1.1.14) like this..

pip2.7 install https://pypi.python.org/packages/a3/d1/eacdedd964ad43fbc4cc7110dac33fbf1dbd7bf50dedb4dc1799c42972c1/SQLAlchemy-1.1.14.tar.gz
Reproduced and started investigating.

Reproduced with this:
```yaml
  series_bug:
    mock:
    - title: Foo.S01E01.1080P.HDTV-Flexget
    metainfo_series: yes
    series:
    - Foo:
        quality: 1080p
```

Resolving patch:
diff --git a/flexget/plugins/filter/series.py b/flexget/plugins/filter/series.py
--- a/flexget/plugins/filter/series.py
+++ b/flexget/plugins/filter/series.py
@@ -256,23 +256,23 @@ class AlternateNames(Base):
     _alt_name_normalized = Column('alt_name_normalized', Unicode, index=True, unique=True)
     series_id = Column(Integer, ForeignKey('series.id'), nullable=False)
 
-    def name_setter(self, value):
+    @hybrid_property
+    def alt_name(self):
+        return self._alt_name
+
+    @alt_name.setter
+    def alt_name(self, value):
         self._alt_name = value
         self._alt_name_normalized = normalize_series_name(value)
 
-    def name_getter(self):
-        return self._alt_name
-
-    def name_comparator(self):
+    @alt_name.comparator
+    def alt_name(self):
         return NormalizedComparator(self._alt_name_normalized)
 
     @property
     def name_normalized(self):
         return self._alt_name_normalized
 
-    alt_name = hybrid_property(name_getter, name_setter)
-    alt_name.comparator(name_comparator)
-
     def __init__(self, name):
         self.alt_name = name
 
@@ -304,23 +304,23 @@ class Series(Base):
     seasons = relation('Season', backref='series', cascade='all, delete, delete-orphan')
 
     # Make a special property that does indexed case insensitive lookups on name, but stores/returns specified case
-    def name_getter(self):
+    @hybrid_property
+    def name(self):
         return self._name
 
-    def name_setter(self, value):
+    @name.setter
+    def name(self, value):
         self._name = value
         self._name_normalized = normalize_series_name(value)
 
-    def name_comparator(self):
+    @name.comparator
+    def name(self):
         return NormalizedComparator(self._name_normalized)
 
     @property
     def name_normalized(self):
         return self._name_normalized
 
-    name = hybrid_property(name_getter, name_setter)
-    name.comparator(name_comparator)
-
     def __str__(self):
         return '<Series(id=%s,name=%s)>' % (self.id, self.name)
 
diff --git a/flexget/utils/database.py b/flexget/utils/database.py
--- a/flexget/utils/database.py
+++ b/flexget/utils/database.py
@@ -198,7 +198,7 @@ def comparator(self):
         return QualComparator(getattr(self, text_attr))
 
     prop = hybrid_property(getter, setter)
-    prop.comparator(comparator)
+    prop = prop.comparator(comparator)
     return prop
 
 


Test patch: