Skip to content

Commit

Permalink
Merge 1c12d66 into 21b8358
Browse files Browse the repository at this point in the history
  • Loading branch information
ratoaq2 committed Jul 23, 2017
2 parents 21b8358 + 1c12d66 commit b9206d1
Show file tree
Hide file tree
Showing 9 changed files with 212 additions and 8 deletions.
3 changes: 3 additions & 0 deletions HISTORY.rst
Expand Up @@ -92,6 +92,9 @@ History
- Added `Extended Surround` as new `audio_profile` possible value.
- Added `EX` as new `audio_profile` possible value
- Added `Opus` as new `audio_codec` possible value
- Changed `size` output to return `guessit.Size` object.
- Added `audio_video_rate` as new possible property.
- Added `video_video_rate` as new possible property.

2.1.4 (2017-06-01)
------------------
Expand Down
18 changes: 17 additions & 1 deletion docs/properties.rst
Expand Up @@ -175,6 +175,13 @@ Video properties
- ``DXVA``


- **video_bit_rate**

Video bit rate (Mbps). Examples: ``25Mbps`` (``<BitRate [25Mbps]>``), ``40Mbps`` (``<BitRate [40Mbps]>``).

- ``[<guessit.BitRate>]`` (object has ``magnitude`` and ``units``)


Audio properties
----------------

Expand All @@ -201,6 +208,13 @@ Audio properties
``Master Audio``


- **audio_bit_rate**

Audio bit rate (Kbps, Mbps). Examples: ``448Kbps`` (``<BitRate [448Kbps]>``), ``1.5Mbps`` (``<BitRate [1.5Mbps]>``).

- ``[<guessit.BitRate>]`` (object has ``magnitude`` and ``units``)


Localization properties
-----------------------

Expand Down Expand Up @@ -260,7 +274,9 @@ Other properties

- **size**

Size (MB, GB, TB).
Size (MB, GB, TB). Examples: ``1.2GB`` (``<Size [1.2GB]>``), ``430MB`` (``<Size [430MB]>``).

- ``[<guessit.Size>]`` (object has ``magnitude`` and ``units``)


- **edition**
Expand Down
1 change: 1 addition & 0 deletions guessit/__init__.py
Expand Up @@ -5,5 +5,6 @@
"""
from .api import guessit, GuessItApi
from .options import ConfigurationException
from .rules.common.quantity import Size

from .__version__ import __version__
2 changes: 2 additions & 0 deletions guessit/rules/__init__.py
Expand Up @@ -24,6 +24,7 @@
from .properties.streaming_service import streaming_service
from .properties.other import other
from .properties.size import size
from .properties.bit_rate import bit_rate
from .properties.edition import edition
from .properties.cds import cds
from .properties.bonus import bonus
Expand Down Expand Up @@ -63,6 +64,7 @@ def rebulk_builder():
rebulk.rebulk(streaming_service())
rebulk.rebulk(other())
rebulk.rebulk(size())
rebulk.rebulk(bit_rate())
rebulk.rebulk(edition())
rebulk.rebulk(cds())
rebulk.rebulk(bonus())
Expand Down
90 changes: 90 additions & 0 deletions guessit/rules/common/quantity.py
@@ -0,0 +1,90 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Quantities: Size
"""
import re
from abc import abstractmethod

import six

from ..common import seps


class Quantity(object):
"""
Represent a quantity object with magnitude and units.
"""

parser_re = re.compile(r'(?P<magnitude>\d+(?:[.]\d+)?)(?P<units>[^\d]+)')

def __init__(self, magnitude, units):
self.magnitude = magnitude
self.units = units

@classmethod
@abstractmethod
def parse_units(cls, value):
"""
Parse a string to a proper unit notation.
"""
raise NotImplementedError

@classmethod
def fromstring(cls, string):
"""
Parse the string into a quantity object.
:param string:
:return:
"""
values = cls.parser_re.match(string).groupdict()
try:
magnitude = int(values['magnitude'])
except ValueError:
magnitude = float(values['magnitude'])
units = cls.parse_units(values['units'])

return cls(magnitude, units)

def __hash__(self):
return hash(str(self))

def __eq__(self, other):
if isinstance(other, six.string_types):
return str(self) == other
if not isinstance(other, self.__class__):
return NotImplemented
return self.magnitude == other.magnitude and self.units == other.units

def __ne__(self, other):
return not self == other

def __repr__(self):
return '<{0} [{1}]>'.format(self.__class__.__name__, self)

def __str__(self):
return '{0}{1}'.format(self.magnitude, self.units)


class Size(Quantity):
"""
Represent size.
e.g.: 1.1GB, 300MB
"""

@classmethod
def parse_units(cls, value):
return value.strip(seps).upper()


class BitRate(Quantity):
"""
Represent bit rate.
e.g.: 320Kbps, 1.5Mbps
"""

@classmethod
def parse_units(cls, value):
return value.strip(seps).capitalize()
48 changes: 48 additions & 0 deletions guessit/rules/properties/bit_rate.py
@@ -0,0 +1,48 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
video_bit_rate and audio_bit_rate properties
"""
import re

from rebulk import Rebulk
from rebulk.rules import Rule, RenameMatch

from ..common import dash, seps
from ..common.quantity import BitRate
from ..common.validators import seps_surround


def bit_rate():
"""
Builder for rebulk object.
:return: Created Rebulk object
:rtype: Rebulk
"""
rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash])
rebulk.defaults(name='audio_bit_rate', validator=seps_surround)
rebulk.regex(r'\d+-?[kmg]bps', r'\d+\.\d+-?[kmg]bps',
conflict_solver=(
lambda match, other: match
if other.name == 'audio_channels' and 'weak-audio_channels' not in other.tags
else other
),
formatter=BitRate.fromstring, tags=['release-group-prefix'])

rebulk.rules(BitRateTypeRule)

return rebulk


class BitRateTypeRule(Rule):
"""
Convert audio bit rate guess into video bit rate.
"""
consequence = RenameMatch('video_bit_rate')

def when(self, matches, context):
for match in matches.named('audio_bit_rate'):
previous = matches.previous(match, index=0,
predicate=lambda m: m.name in ('source', 'screen_size', 'video_codec'))
if previous and not matches.holes(previous.end, match.start, predicate=lambda m: m.value.strip(seps)):
yield match
10 changes: 3 additions & 7 deletions guessit/rules/properties/size.py
Expand Up @@ -7,8 +7,9 @@

from rebulk import Rebulk

from ..common.validators import seps_surround
from ..common import dash
from ..common.quantity import Size
from ..common.validators import seps_surround


def size():
Expand All @@ -17,13 +18,8 @@ def size():
:return: Created Rebulk object
:rtype: Rebulk
"""

def format_size(value):
"""Format size using uppercase and no space."""
return re.sub(r'(?<=\d)[.](?=[^\d])', '', value.upper())

rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash])
rebulk.defaults(name='size', validator=seps_surround)
rebulk.regex(r'\d+\.?[mgt]b', r'\d+\.\d+[mgt]b', formatter=format_size, tags=['release-group-prefix'])
rebulk.regex(r'\d+-?[mgt]b', r'\d+\.\d+-?[mgt]b', formatter=Size.fromstring, tags=['release-group-prefix'])

return rebulk
11 changes: 11 additions & 0 deletions guessit/test/episodes.yml
Expand Up @@ -4065,3 +4065,14 @@
release_group: VLAD
container: mkv
type: episode

? Hotel.Hell.S01E01.720p.DD5.1.448kbps-ALANiS
: title: Hotel Hell
season: 1
episode: 1
screen_size: 720p
audio_codec: Dolby Digital
audio_channels: '5.1'
audio_bit_rate: 448Kbps
release_group: ALANiS
type: episode
37 changes: 37 additions & 0 deletions guessit/test/various.yml
Expand Up @@ -816,3 +816,40 @@
title: Duck Dynasty
type: episode
video_codec: H.264

? Katy Perry - Pepsi & Billboard Summer Beats Concert Series 2012 1080i HDTV 20 Mbps DD2.0 MPEG2-TrollHD.ts
: title: Katy Perry
alternative_title: Pepsi & Billboard Summer Beats Concert
year: 2012
screen_size: 1080i
source: HDTV
video_bit_rate: 20Mbps
audio_codec: Dolby Digital
audio_channels: '2.0'
video_codec: MPEG-2
release_group: TrollHD
container: ts

? Justin Timberlake - MTV Video Music Awards 2013 1080i 32 Mbps DTS-HD 5.1.ts
: title: Justin Timberlake
alternative_title: MTV Video Music Awards
year: 2013
screen_size: 1080i
video_bit_rate: 32Mbps
audio_codec: DTS-HD
audio_channels: '5.1'
container: ts
type: movie

? Chuck Berry The Very Best Of Chuck Berry(2010)[320 Kbps]
: title: Chuck Berry The Very Best Of Chuck Berry
year: 2010
audio_bit_rate: 320Kbps
type: movie

? Title Name [480p][1.5Mbps][.mp4]
: title: Title Name
screen_size: 480p
video_bit_rate: 1.5Mbps
container: mp4
type: movie

0 comments on commit b9206d1

Please sign in to comment.