pax_global_header00006660000000000000000000000064145365537170014532gustar00rootroot0000000000000052 comment=c7b39db259b9c988046eb5200862b87d26d54696 guessit-3.8.0/000077500000000000000000000000001453655371700132255ustar00rootroot00000000000000guessit-3.8.0/.coveragerc000066400000000000000000000002161453655371700153450ustar00rootroot00000000000000# .coveragerc to control coverage.py [run] omit = guessit/__version__.py guessit/test/* [report] exclude_lines = pragma: no cover guessit-3.8.0/.dockerignore000066400000000000000000000002221453655371700156750ustar00rootroot00000000000000__pycache__/ *.py[cod] **/__pycache__/ **/*.py[cod] .benchmarks/ .cache/ .eggs/ *.egg-info/ *.egg .tox/ .coverage .python-version doc/ *.log *.imlguessit-3.8.0/.github/000077500000000000000000000000001453655371700145655ustar00rootroot00000000000000guessit-3.8.0/.github/workflows/000077500000000000000000000000001453655371700166225ustar00rootroot00000000000000guessit-3.8.0/.github/workflows/ci.yml000066400000000000000000000200501453655371700177350ustar00rootroot00000000000000name: ci on: push: ~ pull_request: ~ jobs: ci: if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository runs-on: ubuntu-latest name: Python ${{ matrix.python-version }}${{ fromJSON('["", " (regex)"]')[matrix.regex == '1'] }} strategy: matrix: python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "pypy-3.8", "pypy-3.9", "pypy-3.10" ] regex: [ "0", "1" ] exclude: # regex module doesn't play well with pypy and unicode. - python-version: "pypy-3.8" regex: "1" - python-version: "pypy-3.9" regex: "1" - python-version: "pypy-3.10" regex: "1" # test regex module only with Python 3.9. - python-version: "3.7" regex: "1" - python-version: "3.8" regex: "1" - python-version: "3.10" regex: "1" - python-version: "3.11" regex: "1" - python-version: "3.12" regex: "1" steps: - name: Checkout uses: actions/checkout@v4 - name: Setup python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install Dependencies run: | pip install -e .[dev,test] - name: Install regex run: | pip install regex if: matrix.regex == '1' - name: Pylint run: pylint guessit - name: Test run: | pytest --cov=guessit env: REBULK_REGEX_ENABLED: ${{ matrix.regex }} - name: Codecov uses: codecov/codecov-action@v3 commitlint: if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: wagoid/commitlint-github-action@v5 build-setuptools: if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository needs: [ ci ] runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup python 3.12 uses: actions/setup-python@v4 with: python-version: 3.12 - name: Install Dependencies run: | pip install -e .[dev] - name: Git User config run: | git config --global user.email "action@github.com" git config --global user.name "github-actions" - name: Bump version run: semantic-release version --no-commit --no-tag --no-push env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Build run: python setup.py sdist bdist_wheel - uses: actions/upload-artifact@v3 with: name: guessit-python path: ./dist build-bin-linux: if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository needs: [ ci ] runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup python 3.12 uses: actions/setup-python@v4 with: python-version: 3.12 - name: Install Dependencies run: | pip install -e .[dev] - name: Git User config run: | git config --global user.email "action@github.com" git config --global user.name "github-actions" - name: Bump version run: semantic-release version --no-commit --no-tag --no-push env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Binary run: pyinstaller --dist ./dist guessit.spec - name: Check binary run: ./dist/guessit "Treme.1x03.Right.Place,.Wrong.Time.HDTV.XviD-NoTV.avi" - uses: actions/upload-artifact@v3 if: matrix.regex == '0' with: name: guessit-bin-linux path: ./dist build-bin-windows: if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository needs: [ ci ] runs-on: windows-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup python 3.12 uses: actions/setup-python@v4 with: python-version: 3.12 - name: Install Dependencies run: | pip install -e .[dev] - name: Git User config run: | git config --global user.email "action@github.com" git config --global user.name "github-actions" - name: Bump version run: semantic-release version --no-commit --no-tag --no-push env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Binary run: pyinstaller --dist ./dist guessit.spec - name: Check binary run: ./dist/guessit "Treme.1x03.Right.Place,.Wrong.Time.HDTV.XviD-NoTV.avi" - uses: actions/upload-artifact@v3 with: name: guessit-bin-windows path: ./dist build-bin-macos: if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository needs: [ ci ] runs-on: macos-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup python 3.12 uses: actions/setup-python@v4 with: python-version: 3.12 - name: Install Dependencies run: | pip install -e .[dev] - name: Git User config run: | git config --global user.email "action@github.com" git config --global user.name "github-actions" - name: Bump version run: semantic-release version --no-commit --no-tag --no-push env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Binary run: pyinstaller --dist ./dist guessit.spec - name: Check binary run: ./dist/guessit "Treme.1x03.Right.Place,.Wrong.Time.HDTV.XviD-NoTV.avi" - uses: actions/upload-artifact@v3 with: name: guessit-bin-macos path: ./dist release: if: github.ref == 'refs/heads/master' && github.event_name == 'push' needs: [ commitlint, build-setuptools, build-bin-linux, build-bin-windows, build-bin-macos ] runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup python 3.12 uses: actions/setup-python@v4 with: python-version: 3.12 - uses: actions/download-artifact@v3 with: path: artifacts - name: Copy artifacts to ./dist run: | mkdir -p ./dist mv artifacts/guessit-bin-linux/guessit ./dist/guessit-linux mv artifacts/guessit-bin-macos/guessit ./dist/guessit-macos mv artifacts/guessit-bin-windows/guessit.exe ./dist/guessit-windows.exe mv artifacts/guessit-python/* ./dist - name: Git User config run: | git config --global user.email "action@github.com" git config --global user.name "github-actions" - name: Install python-semantic-release twine run: pip install python-semantic-release twine - name: Bump version run: semantic-release version env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload to pypi run: twine upload --username "__token__" --password "${PYPI_TOKEN}" dist/*.tar.gz dist/*.whl env: PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} - name: Publish release run: semantic-release publish env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Merge master to develop uses: robotology/gh-action-nightly-merge@v1.4.0 with: stable_branch: 'master' development_branch: 'develop' allow_ff: true user_name: github-actions env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} guessit-3.8.0/.github/workflows/mkdocs.yml000066400000000000000000000010351453655371700206240ustar00rootroot00000000000000name: mkdocs on: push: branches: - master jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: 3.x - run: pip install mkdocs mkdocs-material - run: mkdocs build - name: Deploy 🚀 uses: JamesIves/github-pages-deploy-action@v4 with: token: ${{ secrets.GITHUB_TOKEN }} branch: gh-pages folder: site clean: true single-commit: true guessit-3.8.0/.gitignore000066400000000000000000000003761453655371700152230ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] dist/ # Python dist *.egg-info/ .eggs/ build/ # Coverage .coverage # PyEnv .python-version # Tox .tox/ # py.test lastfailed .pytest_cache/ # Jetbrain *.iml .idea/ # docs docs/_build/ guessit-3.8.0/AUTHORS.md000066400000000000000000000010651453655371700146760ustar00rootroot00000000000000Copyright (c) 2011 - 2020, The GuessIt contributors. GuessIt is an opensource project written and maintained by passionate people. If you feel your name should belong to this list, please [open an issue](https://github.com/guessit/guessit/issues) Author and contributors of current guessit version (`2.x`/`3.x`): - Rémi Alvergnat <> - Rato <> Author and contributors of initial guessit version (`0.x`/`1.x`): - Nicolas Wack <> - Ricard Marxer <> guessit-3.8.0/CHANGELOG.md000066400000000000000000004133661453655371700150530ustar00rootroot00000000000000# CHANGELOG ## v3.8.0 (2023-12-14) ### Chore * chore: migrate setup.cfg to pyproject.toml ([`ee9bcbc`](https://github.com/guessit-io/guessit/commit/ee9bcbc2885b636c5d18cb3cea834568d4ef946b)) * chore: remove win_private_assemblies from pyinstaller spec ([`5404433`](https://github.com/guessit-io/guessit/commit/5404433a56444c72f27eb8e4abe9d28224879b59)) * chore(lint): Silence pylint This fixes the following error: ``` ************* Module guessit.rules.match_processors guessit/rules/match_processors.py:7:0: R1710: Either all return statements in a function should return an expression, or none of them should. (inconsistent-return-statements) ``` ([`6a7bb9f`](https://github.com/guessit-io/guessit/commit/6a7bb9f51b9f7ad869767bb1a896a4cb79f3279e)) ### Ci * ci: use twine to upload packages on pypi ([`edf2916`](https://github.com/guessit-io/guessit/commit/edf29168a185ed2a75dcbea642c6912d11e12d77)) * ci: add version bump in release task ([`ce62ca9`](https://github.com/guessit-io/guessit/commit/ce62ca94a263046f83b5f4f5f3d336752b654863)) * ci: avoid mutating repository on semantic-release version command ([`e8273ca`](https://github.com/guessit-io/guessit/commit/e8273ca10d2280dff13a88c37e95f4d21f81baa8)) * ci: add GH_TOKEN environment variables to semantic-release version commands ([`6d7cee7`](https://github.com/guessit-io/guessit/commit/6d7cee7e5728ffcfc9c389218d203fd9a579c7c4)) * ci: remove verbose flag from semantic release commands ([`485d79e`](https://github.com/guessit-io/guessit/commit/485d79e3f0c7c244ed118634bb139358eb8cfc55)) * ci: add wheel to dev dependencies for setup.py bdist_wheel command to work ([`a49278d`](https://github.com/guessit-io/guessit/commit/a49278dd9a7cc4e0205c0f908430946a5e728df4)) * ci: fix semantic-release commands verbose flag ([`0cfb12f`](https://github.com/guessit-io/guessit/commit/0cfb12feb1fdbc7447375f901a61b988273b26a6)) * ci: upgrade checkout action ([`c39ebef`](https://github.com/guessit-io/guessit/commit/c39ebefb245bc59e572fdd2d060e4a5051018081)) ### Feature * feat(other): detect "Dolby Vision" from "DV" ([`6e08770`](https://github.com/guessit-io/guessit/commit/6e087707bf3e1ffbff67a40aa6dbba7703fc41dc)) * feat(streaming_service): add Paramount+ to Streaming Services ([`60920e3`](https://github.com/guessit-io/guessit/commit/60920e39ab02a1ad9988e21a385fe1f241e4c21d)) * feat(streaming_service): add "amzn-cbr" ([`9207379`](https://github.com/guessit-io/guessit/commit/92073791b23cfdc0497106e64a22806f51fdd90a)) * feat: add python 3.12 and pypy 3.10 support ([`401bcc5`](https://github.com/guessit-io/guessit/commit/401bcc566b7fc1d183d873d5d39f72a1ea0156c0)) ### Fix * fix(deprecated): pathlib.Path.__enter__() is deprecated pathlib.Path.__enter__() is deprecated and scheduled for removal in Python 3.13; Path objects as a context manager is a no-op References: https://bugs.python.org/issue39682 https://bugs.python.org/issue46556 https://github.com/python/cpython/commit/06e1701ad3956352bc0f42b8f51c2f8cc85bf378 ([`aa85b2e`](https://github.com/guessit-io/guessit/commit/aa85b2e6bc19c6162d16efbff1d92948cea30ea3)) * fix(deprecated): importlib.resources.read_text() is deprecated ([`cd36bad`](https://github.com/guessit-io/guessit/commit/cd36bad1a4b1fc82dc947309fbe5b3448e5ccaeb)) ### Style * style: fix pylint false positive ([`f62ec8c`](https://github.com/guessit-io/guessit/commit/f62ec8ce7b364f89f79cdafa7935f3acd9266fb2)) ### Unknown * Merge pull request #750 from milkers69/develop feat(Streaming Service): Add Paramount+ ([`bd66746`](https://github.com/guessit-io/guessit/commit/bd6674603463bb34ba31e5270a29911e023231fd)) * Merge pull request #755 from plotski/feat-dv feat(other): Detect "Dolby Vision" from "DV" ([`342aec4`](https://github.com/guessit-io/guessit/commit/342aec4b4e529ab8710ab919104abd702a65bebb)) * Merge pull request #759 from plotski/deprecated/read_text fix(deprecated): importlib.resources.read_text() is deprecated ([`0f40bbf`](https://github.com/guessit-io/guessit/commit/0f40bbf90139f7e565ffc71112adeff8226bc7d0)) * Merge pull request #761 from plotski/deprecated/Path.__enter__ pathlib.Path.__enter__() is deprecated ([`ac846dc`](https://github.com/guessit-io/guessit/commit/ac846dc60edb58fac69f0fb4ec13895c5b28aa46)) * Merge pull request #758 from plotski/streaming-service/AMZN-CBR feat(streaming_service): add "AMZN-CBR" ([`a619e8b`](https://github.com/guessit-io/guessit/commit/a619e8b687ed2372dc30ea58b6ae93f952609fde)) * Merge pull request #762 from guessit-io/python-3.12-support feat: add python 3.12 and pypy 3.10 support ([`e947d1b`](https://github.com/guessit-io/guessit/commit/e947d1ba3dbc3ca0fec14c92880fd86e4e30edf0)) * Merge pull request #760 from plotski/lint/match_processors.strip chore(lint): Silence pylint ([`3113f3e`](https://github.com/guessit-io/guessit/commit/3113f3e2b5047134466c9101fe4921df8f1fbb7f)) ## v3.7.1 (2023-02-20) ### Fix * fix(episode): ignore absolute_episode guess when SxxExx match is available in filepart Close #712 ([`4aa5012`](https://github.com/guessit-io/guessit/commit/4aa5012edbf9f2898a417baec2c301859f5bcd92)) * fix(title): fix title guessing for `Show Name/Season XX/episode.mkv` directories pattern Note that it will grab Show Name as `title` property only if the `Season XX` directory match a season pattern exactly and fully. Close #721 ([`e717928`](https://github.com/guessit-io/guessit/commit/e717928b8544489bf8aa4f6b2866c0c10f3a7a88)) ### Refactor * refactor(title): refactor rule to make pylint pass ([`d931c05`](https://github.com/guessit-io/guessit/commit/d931c05722f35e0ac2c1a6d5290a74380e5603f1)) ### Test * test(episode): add 4400 test ([`1aa10b7`](https://github.com/guessit-io/guessit/commit/1aa10b7f5119169d49fa0f83d3a87ccc7ea7b17c)) ## v3.7.0 (2023-02-18) ### Documentation * docs(contributing): update branch name ([`4af631d`](https://github.com/guessit-io/guessit/commit/4af631df7a7945a7d229919d56c7f4d874750a4a)) ### Feature * feat(week): add week property ([`8309bf1`](https://github.com/guessit-io/guessit/commit/8309bf14e34e17a871e0f496c27cf431aaba18e1)) ### Fix * fix(episode): fix invalid episode range when a weak episode is present before the match Close #719 ([`ff0a327`](https://github.com/guessit-io/guessit/commit/ff0a3271af736f67e7a6a5fd12b92152035ea57b)) * fix(expected): build output from input string for expected_title/expected_group ([`90cc215`](https://github.com/guessit-io/guessit/commit/90cc2156aafee39ef20c3cb3aaf0e26ddcb83933)) * fix(release_group): properly extract group name from format "Title (MediaInfo Individual) [Group]" ([`25bd367`](https://github.com/guessit-io/guessit/commit/25bd367032262dc2a0ee06852c21846202413823)) ### Test * test: remove non-standard mimetype video/x-matroska assertions Close #724 ([`9e4178c`](https://github.com/guessit-io/guessit/commit/9e4178c8d771ae0a01d5a3cd48225225c6de6215)) ### Unknown * Merge pull request #740 from jonbenta/support-individual-crediting-groups Properly extract group name from format `Title (MediaInfo Individual) [Group]`. ([`439ca59`](https://github.com/guessit-io/guessit/commit/439ca59cfe483b1e22f6e2ef5e28728ffe9fe40b)) * Merge branch 'master' into develop ([`9a1d33c`](https://github.com/guessit-io/guessit/commit/9a1d33ce9b7fb88072cbe808dc081246b2afc391)) ## v3.6.0 (2023-02-18) ### Chore * chore(stack): upgrade stack ([`c6b41de`](https://github.com/guessit-io/guessit/commit/c6b41de6b76f56c9d358b870b957ba3a6ab1f919)) ### Feature * feat(audio_codec): detect "DTS:X" (closes #728) ([`2bdd8f5`](https://github.com/guessit-io/guessit/commit/2bdd8f568a0fa6c5eb97e1f29d5e4d488d86a2aa)) ### Fix * fix(edition): improve remastered/restored detection ([`c3611b9`](https://github.com/guessit-io/guessit/commit/c3611b9b26e7f6c1b6bb2d0e9a708843f3242084)) * fix(container): add m2ts to container extensions fix #730 ([`05cca80`](https://github.com/guessit-io/guessit/commit/05cca806530302733377a278c4bb8c200b6a502c)) ### Unknown * Merge pull request #736 from plotski/detect-dtsx feature(audio_codec): detect "DTS:X" (closes #728) ([`58e4fd2`](https://github.com/guessit-io/guessit/commit/58e4fd2856759df077061454e37347418fa29bc4)) * Merge pull request #734 from kannibalox/fix/rm-guessit.io docs: scrub references to guessit.io ([`b694a8b`](https://github.com/guessit-io/guessit/commit/b694a8b99f286b5db3e48f894347fe135b8a7a82)) * Merge pull request #735 from plotski/master fix(edition): improve remastered/restored detection ([`fd9fa4a`](https://github.com/guessit-io/guessit/commit/fd9fa4a9c807f4a1fe30851cdd1b869b483004e2)) * Scrub references to guessit.io Fixes #727 ([`ad5e442`](https://github.com/guessit-io/guessit/commit/ad5e4420dcda41b4190aa2982202bde0d4332def)) ## v3.5.0 (2022-11-01) ### Chore * chore(pylint): fix pylint issues on current version ([`9f60c9c`](https://github.com/guessit-io/guessit/commit/9f60c9c9328e63e6e8df9d97971e25c60e937a48)) ### Feature * feat(dependencies): drop Python 3.6 support ([`47f5718`](https://github.com/guessit-io/guessit/commit/47f57184a9d0a25c1b415638d0b003dad88ce607)) ### Fix * fix(audio_codec): detect "E-AC-3" and "AC-3" ([`72dc12e`](https://github.com/guessit-io/guessit/commit/72dc12e2489d240839a216041ffe47e9dd128b0f)) * fix(typo): fix common typo ([`42a80f0`](https://github.com/guessit-io/guessit/commit/42a80f0992387c96fc120480aaea35e4b3d9f5b8)) ## v3.4.3 (2021-11-20) ### Fix * fix(setuptools): drop usage of test_requires and setup_requires (#720) Close #720 ([`324b38c`](https://github.com/guessit-io/guessit/commit/324b38ce62cd43efc51074dbd8c5e2ed64fc7573)) ## v3.4.2 (2021-11-08) ### Fix * fix(dependencies): use babelfish>=0.6.0 (#711) Close #711 ([`d2c1b01`](https://github.com/guessit-io/guessit/commit/d2c1b010ed0025d62322e681471abe948c923a1d)) ### Refactor * refactor(website): use files instead of open_text of importlib.resources (#716) Close #716 ([`9ce7706`](https://github.com/guessit-io/guessit/commit/9ce7706d847e53bfe7d5c96a8f2f905a610eadbb)) ## v3.4.1 (2021-11-05) ### Fix * fix(other): detect "Open Matte" with non-space separator ([`b52a9d9`](https://github.com/guessit-io/guessit/commit/b52a9d9f0315af68d41c22772d35488d00c4f04e)) * fix(packaging): use stdlib importlib.resources in py 3.7+ importlib.resources is available as a stdlib module since Python 3.7. Use that if it's available, and fall back to the external importlib_resources back for Python < 3.7. ([`1e7b000`](https://github.com/guessit-io/guessit/commit/1e7b0008232e306478d15f7a78d093804d56df3a)) ## v3.4.0 (2021-11-04) ### Chore * chore(oops): don't use format strings ([`cab06ff`](https://github.com/guessit-io/guessit/commit/cab06ffdd32d13bc2a7f30d3c62d658f6df0b603)) ### Ci * ci: upgrade setuptools on python 3.5 and enable pylint on python 3.9 ([`4c06b0c`](https://github.com/guessit-io/guessit/commit/4c06b0c5835563349844c4682b9be025307e18ca)) * ci(coverage): use Codecov and pytest-cov ([`f743298`](https://github.com/guessit-io/guessit/commit/f743298863746079458e30319464da127a6aeb26)) * ci: enhance job names ([`a2f48e2`](https://github.com/guessit-io/guessit/commit/a2f48e2c6fa53876c3909fbfac2acfc927693834)) * ci(coveralls): add --service=github flag ([`c7d7dcb`](https://github.com/guessit-io/guessit/commit/c7d7dcba98b8265ee4da044144a968d9c5bb4483)) * ci(coveralls): add GITHUB_TOKEN environment variable ([`c5a6e07`](https://github.com/guessit-io/guessit/commit/c5a6e07d52cd9077254f2097548ecf918f7f55c8)) ### Documentation * docs: add new properties ([`f1d8f61`](https://github.com/guessit-io/guessit/commit/f1d8f61fb6d326092e6caaf6419696e9419e8a73)) ### Feature * feat(other): add restored support and match 4k-* patterns ([`99c30eb`](https://github.com/guessit-io/guessit/commit/99c30eb3876a4947ff9a4f5126b70195452a64c7)) * feat(other): add 2in1 support ([`0cf07f4`](https://github.com/guessit-io/guessit/commit/0cf07f47559e0061a6df8437510e98e6afe69747)) * feat(python): add python 3.10 support, drop python 3.5 support ([`a8ea88d`](https://github.com/guessit-io/guessit/commit/a8ea88de31dcf642434fa4ab1315df2467a443ca)) * feat(audio_channels): add support for "1.0" audio channels ([`f22e33d`](https://github.com/guessit-io/guessit/commit/f22e33daada06956c085a5784387e5a4527417e6)) * feat(streaming_service): add more streaming services 9Now: https://www.9now.com.au/ AppleTV+: Alternate APTV tag. Binge: https://binge.com.au/ Blackpills: https://blackpills.com/ BluTV: https://www.blutv.com/ Boomerang: https://www.boomerang.com/ Channel 4: Alternate ALL4 tag. Crave: https://www.crave.ca/ Discovery Plus: https://www.discoveryplus.com Disney+: https://www.disneyplus.com/ Facebook Watch: https://www.facebook.com/watch/ Fandor: https://www.fandor.com/ Fox Premium: https://www.foxplay.com/ https://en.wikipedia.org/wiki/Fox_Premium Foxtel: https://www.foxtel.com.au/ GagaOOLala: https://www.gagaoolala.com/en/home hoichoi: https://www.hoichoi.tv/ iflix: https://www.iflix.com/ iQIYI: https://www.iq.com/ MUBI: https://mubi.com/ National Audiovisual Institute: https://madelen.ina.fr/ National Film Board: https://www.nfb.ca/ Nickelodeon: Alternative NICKAPP tag Opto: https://opto.sic.pt/ Oprah Winfrey Network: https://www.oprah.com/ Peacock: https://www.peacocktv.com/ PokerGO: https://pokergo.com Rakuten TV: https://waitforit.rakuten.tv/ The Roku Channel: https://therokuchannel.roku.com/ RUUTU: https://www.ruutu.fi/ Science Channel: https://www.sciencechannel.com/ Sony: https://www.sonyliv.com/ TVNZ: https://www.tvnz.co.nz/ UFC Fight Pass: https://ufcfightpass.com ([`40ce483`](https://github.com/guessit-io/guessit/commit/40ce4831600f915a91cea1e6aba4bcb4cfbc35f0)) * feat(other): add ONA/OAD support ([`0823e37`](https://github.com/guessit-io/guessit/commit/0823e37b382ed3470924a11a5024c0a9a383df30)) * feat(other): add Repack and ReRip followed by a digit support (#653) The digit following those tags now as the base value for `proper_count` property. Close #653 ([`997c5c2`](https://github.com/guessit-io/guessit/commit/997c5c29f4adedcd7568a22b38155e2a8c83bf10)) ### Fix * fix(source): avoid Shots to be guessed as Showtime and TS Close #709 Close #710 ([`de85403`](https://github.com/guessit-io/guessit/commit/de85403dd40b120a3dc52f26960986a2fb482e64)) * fix(screen_size): add 540i ([`1a7db40`](https://github.com/guessit-io/guessit/commit/1a7db40a1f7d06f49db2ac116a1de074049334c0)) * fix(language): fix `language` and `subtitle_languages` in some situations (#696) ([`f19cfda`](https://github.com/guessit-io/guessit/commit/f19cfda856958c4aa81dde36ecbb3df66cdd4b48)) * fix(packaging): use importlib-resources instead of pkgutil ([`a679a6c`](https://github.com/guessit-io/guessit/commit/a679a6c2b05607029a1267b0b217c5696f85df5f)) * fix(other): fix Open Matte when written as is (#689) Close #689 ([`ddf8e77`](https://github.com/guessit-io/guessit/commit/ddf8e772d735bc80940ba3068c5014d79499a618)) * fix(frame_rate): enhance `frame_rate` when ending with `.000` or space separated (#693) ([`dba9cef`](https://github.com/guessit-io/guessit/commit/dba9cef859cb548988e411c2a9d537914da14f4e)) * fix(packaging): use importlib-resources instead of pkg_resources ([`6ef222e`](https://github.com/guessit-io/guessit/commit/6ef222ea2879343c7c3dd04dd081a2ce88a327aa)) * fix(edition): better support for "Criterion" ([`85ac52a`](https://github.com/guessit-io/guessit/commit/85ac52a771f53cc6e44bc48a0c3a017e971c0d71)) * fix(advanced-config): fix removal of custom rebulk rules (#692) Close #692 ([`c2bc1ea`](https://github.com/guessit-io/guessit/commit/c2bc1ea3809626442374f35d018b43e6c775758f)) * fix(streaming_service): make SBS ambiguous SBS is used ambiguously for both: - Special Broadcasting Service (Australia) - SBS Broadcasting Group (previously Scandinavian Broadcasting Systems) White.Wall.S01E01.720p.SBS.WEB-DL.AAC2.0.H.264-GBone.mkv (Scandanavia) Gourmet.Farmer.S05E01.720p.SBS.WEB-DL.AAC2.0.H.264-SOIL.mkv (AU) ([`777d2b5`](https://github.com/guessit-io/guessit/commit/777d2b56764d14431125cccb092978fa726c2453)) * fix(streaming_service): keep pattern to avoid rebuilding rules This causes the `advanced_config` and `self.advanced_config` to differ without any changes to the default config in `api.py`. ([`62f0c0e`](https://github.com/guessit-io/guessit/commit/62f0c0ec5d43074175fffc2aff611b178ddb95a0)) * fix(proper_count): fix proper_count raw output to include all matches (#626) Close #626 ([`1faea7e`](https://github.com/guessit-io/guessit/commit/1faea7e9c8bc9ace9d343976ea1987a5f2628a4f)) * fix(website): fix website when it contains a digit (#659) Close #659 ([`9e39b7e`](https://github.com/guessit-io/guessit/commit/9e39b7e659e87dc3f71b1449a8994ef898212b0e)) ### Refactor * refactor(source): refactor `source` patterns to configuration ([`066a9dd`](https://github.com/guessit-io/guessit/commit/066a9dd448d594e1bdd67a3534c632050a484bd6)) * refactor(pylint): fix pylint issue ([`6e75104`](https://github.com/guessit-io/guessit/commit/6e751042745b9f27346851905a95216a99c41b8b)) * refactor(other): refactor `other` patterns to configuration ([`18b355d`](https://github.com/guessit-io/guessit/commit/18b355d81ad5fd29b2c2f20c5b062f97ac734709)) * refactor(film): move `film` patterns to configuration ([`ab770ef`](https://github.com/guessit-io/guessit/commit/ab770ef43bbcfc2ebfcc88654bad0a128b164587)) * refactor(edition): move `edition` patterns to configuration ([`814a78e`](https://github.com/guessit-io/guessit/commit/814a78e1b307aeab4a357784465371cca6b6ef55)) * refactor(bonus): set bonus property name to rebulk defaults ([`e673a64`](https://github.com/guessit-io/guessit/commit/e673a64dfd69d4df6d5ebc7e1360d10d848d019d)) * refactor(cd): move `cd` patterns to configuration ([`2be8217`](https://github.com/guessit-io/guessit/commit/2be821735be4e71c4c6c6e5ebe09afc6b6fba9ae)) * refactor(bonus): move `bonus` patterns to configuration ([`60c1354`](https://github.com/guessit-io/guessit/commit/60c1354f4c58989ec9e1234b7cec4d3edda2666f)) * refactor(bit_rate): move `bit_rate` patterns to configuration ([`f8c3bb6`](https://github.com/guessit-io/guessit/commit/f8c3bb6712b26c722c1e6d9296a666a724446c41)) * refactor(audio_codec): move `audio_codec` patterns to configuration ([`4ddc5f5`](https://github.com/guessit-io/guessit/commit/4ddc5f5c15391fc3050ef7dd7124ea0c3a5dc20f)) * refactor(audio_profile): move `audio_profile` patterns to configuration ([`30592b7`](https://github.com/guessit-io/guessit/commit/30592b7abd2b00f7163b265c7c7f34bedfb5653d)) * refactor(audio_channels): move more patterns to configuration ([`d7d6732`](https://github.com/guessit-io/guessit/commit/d7d673227b40e8e8c0516c6a52f9d42687d98218)) * refactor(config): move loading of patterns from config to a function ([`80b97a4`](https://github.com/guessit-io/guessit/commit/80b97a450ff6d2be3e491cd4f2ea491754deb400)) ## v3.3.1 (2021-02-06) ### Build * build(ci): add windows and macos binary support ([`4b1fa77`](https://github.com/guessit-io/guessit/commit/4b1fa7748b2a52b5e210faa36c426e5bc7909499)) * build(pyinstaller): add linux binary support ([`d0df79f`](https://github.com/guessit-io/guessit/commit/d0df79f47783ff5d7069d8cbc6149f68e330168e)) ### Ci * ci: configure git before bumping version ([`79014e0`](https://github.com/guessit-io/guessit/commit/79014e0a8f716609a0de736eaa092cee05521953)) ### Documentation * docs: lighten README and update doc index page ([`8e4ba6f`](https://github.com/guessit-io/guessit/commit/8e4ba6f0e4835026e85fca81faba267c95eb31fc)) ### Fix * fix(options): fix custom options.json groups starting/ending (#671) ([`40f43b1`](https://github.com/guessit-io/guessit/commit/40f43b133cf5fb43d79c1e2b886a3620830a4a37)) ## v3.3.0 (2021-02-03) ### Chore * chore(docs): update docs with new `streaming_service` values ([`53f822a`](https://github.com/guessit-io/guessit/commit/53f822a8504fec6fff210a36ce399c1386ff7219)) ### Ci * ci(release): use robotology/gh-action-nightly-merge@v1.3.2 ([`7d2b4bd`](https://github.com/guessit-io/guessit/commit/7d2b4bdbc0697ca592ce329b85f8c29a1084149d)) * ci(mkdocs): use github-pages-deploy-action to deploy docs ([`9ceb019`](https://github.com/guessit-io/guessit/commit/9ceb019c2960b842e91545d4cd14cfaa15ced606)) ### Documentation * docs(readme): avoid mixed-content in github pages ([`2e1f29c`](https://github.com/guessit-io/guessit/commit/2e1f29ca47f8586930ca092b31f2431a1f3df52f)) ### Feature * feat: add `--output-input-string` option (#665) ([`bac6143`](https://github.com/guessit-io/guessit/commit/bac6143559d437edc34e2fde0b77172567e4451d)) * feat(streaming_service): Add `Showtime`, `HBO` and `AppleTV` (#661) ([`dc55eaa`](https://github.com/guessit-io/guessit/commit/dc55eaa6d0cdf9d5552c4dbaaa29c8df8365691c)) * feat(other): add `Hybrid` support (#669) Close #669 Co-authored-by: plotski <plotski@example.org> ([`522af53`](https://github.com/guessit-io/guessit/commit/522af5371cac467dd1f03abb08df9cbf5b409126)) ### Fix * fix(options): avoid appending `None` values to list when merging options (#658) ([`42978c9`](https://github.com/guessit-io/guessit/commit/42978c909c4e5ebb2cc95b94583f80d73759f29a)) * fix(streaming_service): add iT keyword support for iTunes (#669) ([`51e0021`](https://github.com/guessit-io/guessit/commit/51e00217947d8b993bcfb091b012da803245f698)) * fix(streaming_service): fix regex patterns declared with `re:` prefix ([`e02323f`](https://github.com/guessit-io/guessit/commit/e02323f6c1d1e74ef32dfbcfb3ab69e367d11a00)) ### Test * test: make main test less verbose (#665) Co-authored-by: Carey Metcalfe <carey@cmetcalfe.ca> ([`04881fb`](https://github.com/guessit-io/guessit/commit/04881fb62d77a02ab0cc2b552fa8bbdc99807932)) * test(streaming_service): fix tests for `streaming_service` ([`c635022`](https://github.com/guessit-io/guessit/commit/c635022aa2ab88685472a229bcb4cfaef4b7fc79)) * test: enhance test entry result display ([`5136625`](https://github.com/guessit-io/guessit/commit/51366251f0dbeede9e830234c0dd819766aa846c)) * test: pick both `.yml` and `.yaml` files for tests ([`3c46016`](https://github.com/guessit-io/guessit/commit/3c460161008fb74805cfce842943fccc2e1787b7)) ## v3.2.0 (2020-12-23) ### Chore * chore(semantic-release): change release commit message for commitlint concistency ([`dc5f8ed`](https://github.com/guessit-io/guessit/commit/dc5f8edae69998f180ae2b455dbab4eeaa5aa73c)) ### Ci * ci(commitlint): add commitlint to github actions ([`fe06966`](https://github.com/guessit-io/guessit/commit/fe0696646f332f25eb0c686bbb6b9132f377fb80)) * ci: drop TravisCI and ReadTheDocs, replaced by Github Actions and mkdocs ([`384545e`](https://github.com/guessit-io/guessit/commit/384545e35f1919492f92f90757237c8eef0f3529)) ### Feature * feat: add python 3.9 support, drop python 2.7 support ([`2c8b25e`](https://github.com/guessit-io/guessit/commit/2c8b25e77fb424d63f9f73318e85dd96cef865e0)) ### Fix * fix(regex): use rebulk 3+ to have regex module disabled by default Regex module is now disabled by default in rebulk 3+. It can be enabled with REBULK_REGEX_ENABLED=1 in your environment. ([`28658b2`](https://github.com/guessit-io/guessit/commit/28658b27720d9eb48c9f9f0edfadb3247910de43)) ### Unknown * Back to development: 3.1.2 ([`6e4ead1`](https://github.com/guessit-io/guessit/commit/6e4ead187ef98f405487301b7ddc89cb5461ac5d)) * Preparing release 3.1.1 ([`dc8e90d`](https://github.com/guessit-io/guessit/commit/dc8e90dc1e0befbeb9ec7b3166b89cbe64887f21)) * Update classifiers with supported python version ([`6189bac`](https://github.com/guessit-io/guessit/commit/6189bac30a5df5378343068a3d9e1f80301e15ce)) * Update history ([`b0ceada`](https://github.com/guessit-io/guessit/commit/b0ceada768150b23a4a8d93d3e1888fa64ff5ee2)) * Drop python 3.4 support and fix pylint issues ([`59cf946`](https://github.com/guessit-io/guessit/commit/59cf946c53dae7b667157174df15dfb617d3396e)) * Use SafeLoader for yaml.load() Close #642 ([`67058b3`](https://github.com/guessit-io/guessit/commit/67058b36f9b347c23d63133bee155bde6207219d)) * Avoid relying on matroska mimetype in tests This mimetype is not defined in python module internals, so tests may fail in system where this mimetype is not defined in `/etc/mime.types` file. Close #616 ([`ceb826c`](https://github.com/guessit-io/guessit/commit/ceb826c97d761e7cc7b185be7574012119d93154)) * Back to development: 3.1.1 ([`bf85928`](https://github.com/guessit-io/guessit/commit/bf85928cf93f3a23b8c812db4bcb9a8dd2cb441a)) * Preparing release 3.1.0 ([`de53a35`](https://github.com/guessit-io/guessit/commit/de53a35960a446b6a42e5e0361f0ee3e09e48f6c)) * Update history ([`b90835b`](https://github.com/guessit-io/guessit/commit/b90835b04ecbcb5028ce0acec421fecef7de5215)) * Use better version constraints for rebulk ([`bbf27cb`](https://github.com/guessit-io/guessit/commit/bbf27cb7275f3cb6e0073220dd5e6d4518b8f261)) * Add `Variable Frame Rate` value to `other` This replace invalid interpretation of `VFR` tag (French language). ([`250ce16`](https://github.com/guessit-io/guessit/commit/250ce163e305913510a25e1c553bce58dcfe521c)) * Remove `v` from `subtitle_language` prefix in options.json This also adds `vfr` as a synonym for French `language`, and makes `Vita` keyword guessed as `PS Vita` only if match has neighbors. Close #613 ([`553a015`](https://github.com/guessit-io/guessit/commit/553a015629605716d1a404fa62aa96cdb933832b)) * Fix title guessing with 3 fileparts containing language ([`2ea5f62`](https://github.com/guessit-io/guessit/commit/2ea5f62dccae4248e493cff4243c65bcee34b919)) * Never exclude a website if it's in a group Close #597 ([`c907a2c`](https://github.com/guessit-io/guessit/commit/c907a2c873f6de4af8c7c2f847f59d77a6322009)) * Find holes for episode_title with same behavior as main title Close #603 ([`c64868e`](https://github.com/guessit-io/guessit/commit/c64868ea88ff1ed67b97f397e578c5e60c8d6835)) * Fix source validation when more than one pattern match Close #608 ([`b1ec23c`](https://github.com/guessit-io/guessit/commit/b1ec23c0e0b134abc373fdb58715ada0cb21c158)) * Avoid trigger of useless rules consequences ([`ad5220e`](https://github.com/guessit-io/guessit/commit/ad5220e88c3a158f8e964dd5ff667646f82e7d82)) * Validates streaming_service with higher priority Close #602 ([`0e41d2a`](https://github.com/guessit-io/guessit/commit/0e41d2ae039f4ead5a52323d04953bf5d4d77b97)) * Use configured episode words in RemoveWeak rule ([`4886863`](https://github.com/guessit-io/guessit/commit/48868639890fcd496372d4c4fdc866acc7cead3e)) * Fix possible crash in weak episode removal Close #598 ([`d056323`](https://github.com/guessit-io/guessit/commit/d0563238fa7f880fa69883829f9ea91c5d4733cc)) * Bring bak SxxExx tag in chain patterns ([`8511abd`](https://github.com/guessit-io/guessit/commit/8511abd21b2d3e80f2bcaf764b54f7aa7e56f439)) * Disable pylint with python 3.8 ([`5644b9c`](https://github.com/guessit-io/guessit/commit/5644b9c3b7a98d954753c79e1cdb96a6a1301203)) * Migrate to rebulk 2.0.0 This also enhance some rules in season/episodes handling ([`a43966a`](https://github.com/guessit-io/guessit/commit/a43966ab41542d7c397a7981b50ea5f6ffe93a2a)) * Merge pull request #614 from sharkykh/patch-1 Add missing `six` dependency ([`bc65e99`](https://github.com/guessit-io/guessit/commit/bc65e992553a3430898cc97b2cc7135ba8675718)) * Add missing `six` dependency ([`809e175`](https://github.com/guessit-io/guessit/commit/809e175b47e83d51690ded6dd8926abb3ffaecc6)) * Generate test cases dynamically from yml files ([`ca60ce6`](https://github.com/guessit-io/guessit/commit/ca60ce6ec2dcba48b68e77d381e64256af4a5380)) * Add python 3.8 support ([`950ca79`](https://github.com/guessit-io/guessit/commit/950ca79a01972321c97387094bb55e63d39a9ad2)) * Back to development: 3.0.5 ([`1dc0a20`](https://github.com/guessit-io/guessit/commit/1dc0a207917b572b6aaf3e7b6ee48b1bf0f58f86)) * Preparing release 3.0.4 ([`b1651ff`](https://github.com/guessit-io/guessit/commit/b1651ffba7399641a68e3da50cb34504067de02f)) * Fix pyroma issues ([`15907c6`](https://github.com/guessit-io/guessit/commit/15907c6fe291b6cdb17de47c238856057717545e)) * Update history ([`758e487`](https://github.com/guessit-io/guessit/commit/758e4876793c082684bfddc64f8983800193c4c1)) * Use official python alpine for docker image ([`ce8a3ea`](https://github.com/guessit-io/guessit/commit/ce8a3eab42cb312675725a5a3b5e0de9357e40bb)) * Add more streaming sites (#595) Add more streaming sites This also adds 30/1.001 Framerate and sorts some options ([`fd8af5f`](https://github.com/guessit-io/guessit/commit/fd8af5f40a6d6e71d8d35896371da69e82259eac)) * #593: Added Extras detection as other property ([`74dbb0c`](https://github.com/guessit-io/guessit/commit/74dbb0c7420cee18969926aba9cc06b8959daa0e)) * Added suggested_expected method to the API to support apps that uses guessit as a library ([`5fbf3a8`](https://github.com/guessit-io/guessit/commit/5fbf3a8217d4ee86428c8499ad65039e5072185a)) * Fixes #358. Added 'This is Us' to the default expected_title configuration ([`217e7e4`](https://github.com/guessit-io/guessit/commit/217e7e46c166a2c974b6c8552cbec6d14d851ec2)) * #590: 'Fixes Show Name/Season SS/Ep. EE - Title' pattern ([`4174267`](https://github.com/guessit-io/guessit/commit/4174267548e890baca608fc53ad8617de9f03be0)) * Fixes #591. Keeping separators for single characters ([`cdea674`](https://github.com/guessit-io/guessit/commit/cdea6741f5698ea26f674289260a2220e888d96a)) * #408: Fixes REAL and PROPER tags scenarios ([`53e8e86`](https://github.com/guessit-io/guessit/commit/53e8e8626e9b6cfd86251a05e2f07f35bb32dada)) * Minor indentation fix ([`240ca61`](https://github.com/guessit-io/guessit/commit/240ca61c13030713014dcc2d3e3b8944896a7377)) * #588: Fixes wrong 3D detection ([`a6f234f`](https://github.com/guessit-io/guessit/commit/a6f234f404c639a4fde74d37f63def7b27e2b01e)) * #574: Improve language detection ([`44f7d60`](https://github.com/guessit-io/guessit/commit/44f7d60e0894ef0cb0f1654e2b320365f1076468)) * #587: Enhanced source detection to avoid wrong match of the word web ([`6a28a3a`](https://github.com/guessit-io/guessit/commit/6a28a3a936278b2b0324b4d3271f887f9d18bed6)) * Fixes wrong lambda detected by pylint ([`a016b27`](https://github.com/guessit-io/guessit/commit/a016b270c9a656356b2dbad7174aa364954b05b8)) * #585: improve title detection when between brackets ([`0e1dc09`](https://github.com/guessit-io/guessit/commit/0e1dc09aa3a73ad95a4787e84fd1e35e5445245f)) * #584: Added DC Universe to streaming_service. Making streaming_service configurable ([`9d422cc`](https://github.com/guessit-io/guessit/commit/9d422ccefaff77fc981d1781eb79de5420b91b79)) * #551: Fixes wrong bonus detection ([`4c10925`](https://github.com/guessit-io/guessit/commit/4c10925e97046b8801f4d07800fae5cd6500dc37)) * Updating documentation and history ([`05a0c96`](https://github.com/guessit-io/guessit/commit/05a0c96cdcd347cadfac08752bb45409fb94dffa)) * #547: Added 540p to screen_size ([`a164b6a`](https://github.com/guessit-io/guessit/commit/a164b6a2c33dc1e2f153ae944079f259b41ca1db)) * Fixing pylint issues in python 2.7 ([`a8741fe`](https://github.com/guessit-io/guessit/commit/a8741fe2ca369b771a9e1bc52f7967966b3d8546)) * Added regression tests for #583 ([`c0ffdc3`](https://github.com/guessit-io/guessit/commit/c0ffdc382dbb2ed43aada7d38b7bd21d43713dd5)) * Ignoring pylint issues that conflicts with python 2.7 and 3.4+ ([`e74cc1b`](https://github.com/guessit-io/guessit/commit/e74cc1b09063b4e6d8610209d56482471cf08eec)) * #578: Add back common words that conflicts with existing allowed_languages. Added test scenarios for all previous common words. ([`4e874bc`](https://github.com/guessit-io/guessit/commit/4e874bcdb6219078549652b573e8eea5756bfbd6)) * #556: Fixed 6.0 audio_channel detection. Make audio_channel detection configurable ([`315ae4d`](https://github.com/guessit-io/guessit/commit/315ae4d48236ba315319874efbc018eb7d03b43b)) * Back to development: 3.0.4 ([`5c2cfee`](https://github.com/guessit-io/guessit/commit/5c2cfeee519f3027588a5f5afafc5eb22ffbd439)) * Preparing release 3.0.3 ([`4deefbd`](https://github.com/guessit-io/guessit/commit/4deefbdb8ebd93bdf8e34f9fc1299a3979b770e7)) * Update history ([`bcc2985`](https://github.com/guessit-io/guessit/commit/bcc29856fe46c6044b9d9befcb69da7a73cadebc)) * Add {season}e{episode} pattern Close #579 ([`4811655`](https://github.com/guessit-io/guessit/commit/4811655c50e931b2b8d1b849c4d772be36a1fd77)) * Fix pylint errors ([`42f9791`](https://github.com/guessit-io/guessit/commit/42f9791e03e7941e360c2b4fe8ec623f69dc82dc)) * Move some patterns from Proper to Fix Close #538 ([`319f698`](https://github.com/guessit-io/guessit/commit/319f69871c03cdb1c212f16e4c9647a59446db9b)) * Add MP2 audio_codec value Close #539 ([`22a70aa`](https://github.com/guessit-io/guessit/commit/22a70aa8a60c1158b51c61e71813dd84f6b7742b)) * Fix missing property when episode_details keyword in title Close #555 ([`add43a0`](https://github.com/guessit-io/guessit/commit/add43a0936b7a847e3ba029f22f4ecc8ba221bd6)) * Remove serie(s) from season marker and fix wrong season/year in episode mode Close #561 ([`12dc120`](https://github.com/guessit-io/guessit/commit/12dc120e2c3a8f3114c190053d165bbede1808a0)) * Fix parent folder ending with a digit detected as title Close #565 ([`c5d938d`](https://github.com/guessit-io/guessit/commit/c5d938dce703464c062f00af432529d32d6f8172)) * Fix false release_group matches with --expected-title option Close #564 ([`01591ae`](https://github.com/guessit-io/guessit/commit/01591aeb58661ab51349016dd9db98cebfccf061)) * Fix configuration docs ([`b05aa64`](https://github.com/guessit-io/guessit/commit/b05aa64c736efcd9fadae81d415795e6104f2a3a)) * Back to development: 3.0.3 ([`b20d44e`](https://github.com/guessit-io/guessit/commit/b20d44ee688c5948dd653c184225b9559988c20e)) * Preparing release 3.0.2 ([`946724d`](https://github.com/guessit-io/guessit/commit/946724d2aa8fc0cb1e222776a178b30d4b8d84b8)) * Fix regression when passing options as string to python module ([`80cae9b`](https://github.com/guessit-io/guessit/commit/80cae9b0b136d7bd3a4dd50f21c58928baf0579d)) * Add advanced property to Rebulk Match object Close #282 ([`8fb54ee`](https://github.com/guessit-io/guessit/commit/8fb54ee4b49e5db1bb5cf50768dcd024faa2be2c)) * Add authors file Close #567 ([`8d24c99`](https://github.com/guessit-io/guessit/commit/8d24c99bf0eb593641da11200ec2c638eae84cd9)) * Update usage in README ([`169cc80`](https://github.com/guessit-io/guessit/commit/169cc806e760b6294ee3ed0b45c822390fcf59c9)) * Fix python2 regression introduced in previous commit ([`2c67c1d`](https://github.com/guessit-io/guessit/commit/2c67c1d43906a0a3787c8f3b291f43a7e9420b6d)) * Enhance configuration files and ensure consistent behavior CLI/module This make loading of user default configuration file, configuration files, and custom configuration files easier to reason about. This also allow overriding advanced_configuration at runtime through options dict. Rebulk rules are now lazily rebuilt when advanced_configuration is changed since previous call. Close #553 ([`74195f7`](https://github.com/guessit-io/guessit/commit/74195f745ca514b1bbb564d6ef28ae3d96d23dda)) * Back to development: 3.0.2 ([`b00fea6`](https://github.com/guessit-io/guessit/commit/b00fea6f3da484a42239e9c288e70cc50b18feae)) * Preparing release 3.0.1 ([`71c06a1`](https://github.com/guessit-io/guessit/commit/71c06a1323f9c9660ecce6ed659fdec7d72b2a68)) * Update changelog ([`7da07d8`](https://github.com/guessit-io/guessit/commit/7da07d8a73456ef8ac7140a1c6d0e43491280570)) * Fix issue with dotted titles Close #546 ([`5abfbb0`](https://github.com/guessit-io/guessit/commit/5abfbb0b927430305c15e5cc374ab835ac7f4404)) * Fix issue with ES audio_profile breaking titles Close #566 ([`43849f0`](https://github.com/guessit-io/guessit/commit/43849f078f9f3aa3f3fb750cf54dedbce08ebd10)) * Add more video_profile to avoid confusion with some release_group Close #569 ([`2121da8`](https://github.com/guessit-io/guessit/commit/2121da89a43dfbb782d2a9e3a99eaee80bacf17a)) * Add support for mk3d container Close #568 ([`c64c54d`](https://github.com/guessit-io/guessit/commit/c64c54d33edf790d4135193c6c1bc960e18baddb)) * Fix possible crash for some release names Close #573 Close #570 ([`c6701f9`](https://github.com/guessit-io/guessit/commit/c6701f987ed4cbad83c2cd67c9107b52337d6b97)) * Remove Bonus and Extras from episode_details Close #558 ([`ab1da59`](https://github.com/guessit-io/guessit/commit/ab1da599cf250a6cf0987770974dcbe82ab87ff8)) * Merge pull request #576 from labrys/patch-1 Close resource after usage ([`fb0e55e`](https://github.com/guessit-io/guessit/commit/fb0e55ecf945e08a8154a6c8685b476826ab123a)) * Add python 3.7 support and fix pylint issues ([`85e0ab4`](https://github.com/guessit-io/guessit/commit/85e0ab4b51112fc1879512bc034aa9023749634c)) * Close resource after usage This closes the resource 'tlds-alpha-by-domain.txt' after usage. Fixes #575 ([`b1d3f6f`](https://github.com/guessit-io/guessit/commit/b1d3f6fcb05090ec898836d0d5a5e844b478cd4b)) * Back to development: 3.0.1 ([`a54e8ac`](https://github.com/guessit-io/guessit/commit/a54e8acdd37ba3fac2be3c651e2d22f8742608c6)) * Preparing release 3.0.0 ([`ee409f4`](https://github.com/guessit-io/guessit/commit/ee409f47fe0feefefc7dcffa8a4b6dad2f30a25d)) * Update history ([`86c894a`](https://github.com/guessit-io/guessit/commit/86c894ac047f6550a8b017d42df9bdcbcd932261)) * Rename "Analogue HDTV" to "Analog HDTV" ([`c5f788c`](https://github.com/guessit-io/guessit/commit/c5f788c54612825ee199038d979ab9e545e4e6e8)) * Fix crash when using pathlib.Path object on python 3.4 & 3.5 ([`5ff3cc0`](https://github.com/guessit-io/guessit/commit/5ff3cc06b7301dc211c18b942c63cd250e9c1a3a)) * Allow advanced guessit configuration # Conflicts: # guessit/api.py ([`8db121b`](https://github.com/guessit-io/guessit/commit/8db121b5c9b5204f3a89dc07aac623f2d48d5ac5)) * Add pypy3 to tox and TravisCI tests ([`29311bc`](https://github.com/guessit-io/guessit/commit/29311bcb015c3e4b968904a336eca0938bc6a80f)) * Merge branch 'pulls/543' into develop ([`5c64714`](https://github.com/guessit-io/guessit/commit/5c647148dd86b7fc36c6045c3406b4cdf4a24d6e)) * Adds support for path-like objects (e.g. pathlib.Path in python >=3.4) ([`6beca82`](https://github.com/guessit-io/guessit/commit/6beca82dc376344a4be400fa8e7dbb3bbea15971)) * Add .pytest_cache directory to .gitignore ([`8ae6b1b`](https://github.com/guessit-io/guessit/commit/8ae6b1b1ccf0c7d50e62bf8b77f83f184204a297)) * Fix spelling mistake in docs ([`04cc163`](https://github.com/guessit-io/guessit/commit/04cc16351803e3eeb35af9cc3dee5d227a283662)) * Fixed rebase issue ([`e49d6f5`](https://github.com/guessit-io/guessit/commit/e49d6f55934649076180b7d92ad23e8d6876023e)) * Updating docs about advanced configuration ([`e357afb`](https://github.com/guessit-io/guessit/commit/e357afbe3bfdbf78fef4854d74590bedbd33747f)) * Fixes #505: don't blacklist languages inside a language group ([`f507608`](https://github.com/guessit-io/guessit/commit/f5076089c22a9331ea2795f93796a442270793f0)) * Reducing common_words to bare minimum ([`c527b83`](https://github.com/guessit-io/guessit/commit/c527b836ecb3d6c800ad31726fadd114afd07369)) * Fix last scenario from #342 ([`4d6c1e6`](https://github.com/guessit-io/guessit/commit/4d6c1e64e69b429e94932f348c44f19bbb4d3671)) * Updating history ([`8581eae`](https://github.com/guessit-io/guessit/commit/8581eae75c545ce0201353e6a8b4adb1da35b5c2)) * Adding language and subtitle affixes to advanced configuration ([`1f891c6`](https://github.com/guessit-io/guessit/commit/1f891c69c3ec2b68a2186e9948d5f0064e73692f)) * Rename to advance configuration. Add common words to advanced configuration. Make sure that adv configuration is always loaded even if no-embedded-config is specified. ([`5480640`](https://github.com/guessit-io/guessit/commit/5480640dd0d2b0d222ffcdce79de0f59677cf662)) * Externalize guessit advanced configuration. Resolves #342 and #277 ([`f135c2e`](https://github.com/guessit-io/guessit/commit/f135c2e1f2d1480eee459cd750752d0fd8ee28b9)) * Merge pull request #536 from guessit-io/feature/additional-other-detections Detect sample, proof, obfuscated and repost ([`994bdba`](https://github.com/guessit-io/guessit/commit/994bdba5e62ce2fa02dc544e520ab058c0931274)) * Fixes #433 and #516: Detecting sample, proof, obfuscated and repost as other properties ([`9f90bc3`](https://github.com/guessit-io/guessit/commit/9f90bc326c12283ca5c260cd56440adb23045eb9)) * Merge pull request #534 from guessit-io/feature/screen-size-see-conflict Improving screen_size and SEE conflict resolution ([`07f4e23`](https://github.com/guessit-io/guessit/commit/07f4e239c5fadff2bdbcda5a0e2f2daf6f993b3e)) * Fixes #533: Improving screen_size and SEE conflict resolution ([`52bef12`](https://github.com/guessit-io/guessit/commit/52bef127b33a28850efb1598b7031ffe4ffa59eb)) * Merge pull request #529 from guessit-io/feature/docker-locale Docker locale configuration ([`3afb850`](https://github.com/guessit-io/guessit/commit/3afb850be95698a46f1c878be408ddf7f671b408)) * Fixes #520: Docker locale configuration ([`9956426`](https://github.com/guessit-io/guessit/commit/99564269de22ca531c3be2e994bcd481fa1c75af)) * Update history and docs: frame_rate property added ([`c902963`](https://github.com/guessit-io/guessit/commit/c9029634f077affaf5c2f615dc31db3181f3b52d)) * Merge pull request #528 from guessit-io/feature/frame_rate Detect frame rate as frame_rate property ([`32b336a`](https://github.com/guessit-io/guessit/commit/32b336a4a570067ffda0cb5f8d6f480e8d1c9911)) * Detect frame rate as frame_rate property. Fixes #480 ([`e0f497d`](https://github.com/guessit-io/guessit/commit/e0f497d116484ddba066db5861f27f42491f8b8f)) * Fix yaml representation for Quantity objects + minor fix in bit_rate detection ([`bae97df`](https://github.com/guessit-io/guessit/commit/bae97dfd61cced6533796cd85980b70a02ab66c4)) * Fixes #254: Incorrect detection with adult content ([`c9ef77f`](https://github.com/guessit-io/guessit/commit/c9ef77fce8c38badeaa737d4cc7822050b4d6ff9)) * Keep order in yaml output ([`dc5398d`](https://github.com/guessit-io/guessit/commit/dc5398d69590e57ffedcf9e0ccd481756176c8fa)) * Fixes for #519: Added 1440p screen_size, improved bit_rate detection and subtitle_language detection ([`1cc9b20`](https://github.com/guessit-io/guessit/commit/1cc9b200ad1e791f01647791519c7ec1dd264698)) * Merge pull request #527 from guessit-io/feature/fix-episode-conflict-audio-channels Fix episode not detected due to conflict with audio_channels ([`15f4368`](https://github.com/guessit-io/guessit/commit/15f43688459fae4d5ab5d0625dd98a4c4c8b2b65)) * Fix #521: Episode not detected due to conflict with audio_channels ([`f52d525`](https://github.com/guessit-io/guessit/commit/f52d525b0ce4f7684614b451164a1eb75ca39a67)) * Merge pull request #526 from guessit-io/feature/fix-season-range-fileparts Fix season range in multiple fileparts ([`d93c522`](https://github.com/guessit-io/guessit/commit/d93c522a445a2444e2603dcb3fc24c69db62687d)) * Fixes #522: Season range in multiple fileparts ([`1487548`](https://github.com/guessit-io/guessit/commit/14875483d6e16800d437f8eeeda2d8f4794e604b)) * Merge pull request #525 from guessit-io/feature/fix-line-detection Fix Line detection ([`4140137`](https://github.com/guessit-io/guessit/commit/4140137f2b9b56e312b7e918bb2407ed1b38cd75)) * Fixes #524: Line detection ([`0377fd0`](https://github.com/guessit-io/guessit/commit/0377fd06081409fefec98ab2f15410d52816dc34)) * Merge pull request #513 from guessit-io/feature/enable-disable-properties Proposal for enabling/disabling properties ([`819033f`](https://github.com/guessit-io/guessit/commit/819033fec7a96ac7d262073ec1d90b16d5308d09)) * Update HISTORY.rst ([`919e839`](https://github.com/guessit-io/guessit/commit/919e839200741106a243878dba2e1be8efa50116)) * Updating documentation ([`9fabc29`](https://github.com/guessit-io/guessit/commit/9fabc29878b271aa53101907639ea574355be9dd)) * Merge pull request #517 from guessit-io/feature/fix-pylint-inconsistent-return-statements Handle new pylint rule: inconsistent-return-statements ([`5e83529`](https://github.com/guessit-io/guessit/commit/5e835292da9cf6d21aeff3b9ced936093d4a85fa)) * Adding tests for include/exclude option with required fixes ([`2ce6443`](https://github.com/guessit-io/guessit/commit/2ce6443e334a8c2ba544ea5a68de0fce4e0641c6)) * Change is_enabled to is_disabled ([`0506a97`](https://github.com/guessit-io/guessit/commit/0506a97505d70d9a31c8dda773c6a6779dcbbb49)) * Fixing failing tests ([`a70d5c9`](https://github.com/guessit-io/guessit/commit/a70d5c91461a02118480a75ed8333f7879f679ff)) * Proposal for #231 enabling/disabling properties ([`7987e29`](https://github.com/guessit-io/guessit/commit/7987e29e56fd05625cc804c637cbcc2ccaf5a0b4)) * Also handle too-many-locals ([`53f6762`](https://github.com/guessit-io/guessit/commit/53f6762734b11694d03511c6da718ab860e5d0ec)) * Handle new pylint rule: inconsistent-return-statements ([`120809a`](https://github.com/guessit-io/guessit/commit/120809a14a0742e98373ce792f0cfb6d302a0cc9)) * Merge pull request #515 from guessit-io/feature/remove-mimetype-from-yaml-files Removing mimetype from yaml files to prevent failing tests ([`eefa208`](https://github.com/guessit-io/guessit/commit/eefa208308a7a32c97753c035bceecc1a1160be6)) * Fixes #498: Removing mimetype from yaml files to prevent failing tests in different environments ([`56eb296`](https://github.com/guessit-io/guessit/commit/56eb296edde03a532f0970a8b1ddebc6c1c15f81)) * Merge pull request #514 from guessit-io/feature/fix-maximum-recursion-depth-navigablestring Fix maximum recursion depth exceeded ([`66f447f`](https://github.com/guessit-io/guessit/commit/66f447f8474ba706483daae1000f145fa839f041)) * Fixes #247: Deep clone doesn't work with NavigableString from beautifulsoup4. Making sure guessit input is either binary or str. ([`e500169`](https://github.com/guessit-io/guessit/commit/e5001691a243f1d90a7df283074d06c8c5b1cbcc)) * Merge pull request #512 from guessit-io/feature/enhance-language-country-detection Further enhancement in countries/language detection ([`2e4ff8c`](https://github.com/guessit-io/guessit/commit/2e4ff8cbd90ce0ca61250d8b7e6e6219f03161bb)) * Fixes #500 and partially fixes #505: Adding AU to default countries and dropping pt and au from blacklisted words since they're not needed anymore. ([`91a3bf9`](https://github.com/guessit-io/guessit/commit/91a3bf9fdedc0213e0f1dae9f5aa6493cc64b11b)) * Merge pull request #511 from guessit-io/feature/ascii-error Ascii error fix and dash separated release group detection fix ([`3ac61f1`](https://github.com/guessit-io/guessit/commit/3ac61f1e91d9c4bea0719672b03e11fec55a4f3a)) * Fixes #492 and fixes dash separated release group detection ([`5b19fd8`](https://github.com/guessit-io/guessit/commit/5b19fd8e4234a976ff7c6acfd33f0aa122a63d70)) * Merge pull request #510 from guessit-io/feature/fix-movie-with-season-word Disable season pattern when type is movie ([`0129424`](https://github.com/guessit-io/guessit/commit/0129424aa4274673e42ac429a532fddd7cde9c08)) * Fix for #465: Disable season pattern when type is movie ([`6ce2ca6`](https://github.com/guessit-io/guessit/commit/6ce2ca6031861cfe09686b68247541fd6aba9b73)) * Removing duplicated values from streaming_services ([`7e8c12c`](https://github.com/guessit-io/guessit/commit/7e8c12c47849bf98612b5047e48a9012603d0138)) * Organizing streaming_services. Updating docs and history. ([`f577483`](https://github.com/guessit-io/guessit/commit/f57748313fd45d70b502eeac6bee24bf43fb5eb3)) * Merge pull request #483 from fernandog/feature/stream_service_more Add missing stream services ([`6dccecd`](https://github.com/guessit-io/guessit/commit/6dccecd1139dffe58e44d6eeba7730d61cc39a57)) * Fix streaming_service validator rule ([`8db9ddc`](https://github.com/guessit-io/guessit/commit/8db9ddc520340f6b37e993e95d090f57d5b3d634)) * Merge remote-tracking branch 'origin/develop' into feature/stream_service_more # Conflicts: # guessit/rules/properties/streaming_service.py # guessit/test/episodes.yml ([`23da643`](https://github.com/guessit-io/guessit/commit/23da6435765d69ad552b843416787168144c7637)) * Merge pull request #491 from guessit-io/feature/auto-episode-prefer-number Automatically use --episode-prefer-number / absolute_episode ([`2a14375`](https://github.com/guessit-io/guessit/commit/2a14375a6debff33c30dd27c48d2781ee76ce732)) * Merge pull request #487 from guessit-io/feature/dm-source Detect Digital Master source ([`8d1bf08`](https://github.com/guessit-io/guessit/commit/8d1bf08308ec5b77215921e38deafeacd67fa35a)) * Fix merging issue + failing test ([`7b2e678`](https://github.com/guessit-io/guessit/commit/7b2e6780ffff27f4929ec9360640883bbd83149b)) * Fix lambda expressions. Fix tag list shouldn't be modified to not affect subsequent calls. ([`827bdce`](https://github.com/guessit-io/guessit/commit/827bdce1290b24b7385f214d52a33ff4581555d8)) * Fix for #423: automatically use --episode-prefer-number. Added absolute_episode ([`e718802`](https://github.com/guessit-io/guessit/commit/e71880263afee5e4acebd1a28b4bc39a1c2fda23)) * Fix for #384: Detect Digital Master source ([`16fd9ee`](https://github.com/guessit-io/guessit/commit/16fd9eee5605111f1217bd3402079b63f4c437cf)) * Merge pull request #486 from guessit-io/feature/additional-audio-video-codecs Additional audio and video codecs ([`774454b`](https://github.com/guessit-io/guessit/commit/774454bc25f6dc67707b0ff1516daa52a2884e5e)) * Fixing failing tests ([`66f4d19`](https://github.com/guessit-io/guessit/commit/66f4d19888ba0f8658ba2283296c369f3488c88c)) * Merge pull request #485 from guessit-io/feature/disc-detection Detect disc numbers ([`7bd17b9`](https://github.com/guessit-io/guessit/commit/7bd17b9cec72110b6edd91f848e660867286b355)) * Fix failing test ([`f6f64a8`](https://github.com/guessit-io/guessit/commit/f6f64a8e83d4bc545065e73b6e74bbc809f36cf1)) * Fix for #482: VP9 video codec. Plus additional common audio and video codecs. ([`1281d8c`](https://github.com/guessit-io/guessit/commit/1281d8cb80a014ea029d3805c5028e2f8f08c6c7)) * Fix for #401: Detect disc numbers ([`413d8f6`](https://github.com/guessit-io/guessit/commit/413d8f6779724ca0d8c47849ac02abd121cff437)) * Merge pull request #484 from guessit-io/feature/size-output Enhance size output value / Detect bit rate ([`8598a54`](https://github.com/guessit-io/guessit/commit/8598a549e7f45791296ceddb05cc73ac620e2eb9)) * Adding bit rate detection. Fixes #251 and #477 ([`5505f74`](https://github.com/guessit-io/guessit/commit/5505f74a995bafdfab8330e97eee9624fffd9e1e)) * Using class Size instead of Quantity. ([`ed6db27`](https://github.com/guessit-io/guessit/commit/ed6db27ddcd5f0fcd7a21d95c4bf44814f2ed4aa)) * Fix for #481: Enhance output value ([`0bda2a7`](https://github.com/guessit-io/guessit/commit/0bda2a73bd83d1e0b4358acdef4449e3edb41d59)) * Merge pull request #476 from guessit-io/feature/default-allowed-languages-countries Add default and configurable list of allowed languages and countries ([`d8f1c89`](https://github.com/guessit-io/guessit/commit/d8f1c89ef0f0cdbb49b50c1f31f2a039545bfc58)) * Fixing failing test ([`0570dfe`](https://github.com/guessit-io/guessit/commit/0570dfee3e96ab2ff89738ec38e19388b20dfe28)) * Add more test scenarios for #259 ([`02df006`](https://github.com/guessit-io/guessit/commit/02df006cdf6cf681952e14f3aa3ce3e087f68694)) * Add test scenario for #489 ([`44aa38b`](https://github.com/guessit-io/guessit/commit/44aa38bf0725c9c686dcda583a77b5437abaee85)) * Improved release group detection to also fix #196 ([`035b4b9`](https://github.com/guessit-io/guessit/commit/035b4b9488e8e831178306cbcaf0eda5070f7a9a)) * Country and languages can be disabled when allowed list is empty ([`4edadb3`](https://github.com/guessit-io/guessit/commit/4edadb3e07232106b494acf87314fa632663704b)) * Added languages from #462: flemish and swiss german ([`0734d93`](https://github.com/guessit-io/guessit/commit/0734d932889f519aa6b06006057547c88ba0ebcd)) * Avoid language and country detection is allowed list is empty. Simplifying language converter ([`fa61e7b`](https://github.com/guessit-io/guessit/commit/fa61e7b09be47e81ef589899ab59493cc7ae07f8)) * Fix and scenarios for #259 ([`e4a15d2`](https://github.com/guessit-io/guessit/commit/e4a15d20905e5042e483025ebf50ac0fcf5884db)) * Fix and scenarios for #258 ([`bc5559c`](https://github.com/guessit-io/guessit/commit/bc5559cb4159c885578bdb81d8df079f64ff3b86)) * Updating HISTORY.rst ([`3132fbc`](https://github.com/guessit-io/guessit/commit/3132fbcce694340bc331bdacec637bc64cb850ee)) * Fix for #458: Remove conflicting matches in release group ([`63a1779`](https://github.com/guessit-io/guessit/commit/63a1779d764289aa6f48fb39f99bcb6001b16752)) * Fix for #296: Add default and configurable list of allowed languages and countries. ([`5df9ca3`](https://github.com/guessit-io/guessit/commit/5df9ca32799d125b5989f7def8198d49bad7f68b)) * Merge pull request #479 from guessit-io/feature/aspect-ratio Add aspect_ratio and validate screen_sizes ([`a4209c9`](https://github.com/guessit-io/guessit/commit/a4209c90b4e52b1202a186f574282c924da69207)) * Travis still not supporting py37 ([`4e8fab6`](https://github.com/guessit-io/guessit/commit/4e8fab6329959cfaa5549c0c9853ca039aa6619a)) * Updating travis with supported python versions ([`f3b2d8b`](https://github.com/guessit-io/guessit/commit/f3b2d8b22fdb888b103847d4aaca28679bdd4761)) * Updating supported python versions: dropped py33 and added py37 ([`4f7c468`](https://github.com/guessit-io/guessit/commit/4f7c4683959d94755850c60d1ed431a15f1567fe)) * Fixing broken build due to pytest-capturelog. Its fork pytest-catchlog was merged into pytest core. ([`12a6328`](https://github.com/guessit-io/guessit/commit/12a6328d52feeaefaaf56ead64ed2f8693e21845)) * Merge pull request #488 from guessit-io/feature/prefer-rightmost-filepart File and folder name: fine tuning marker comparator ([`9b07137`](https://github.com/guessit-io/guessit/commit/9b07137a2aadf3cf6c6ea318d97a3be32154e7e9)) * Add Amazon keyword for Amazon Prime streaming service Close #495 ([`6a74e05`](https://github.com/guessit-io/guessit/commit/6a74e05ac7c2931a40ca22046338ceb3fe5cd65a)) * Added additional test scenario ([`5198832`](https://github.com/guessit-io/guessit/commit/51988323c9977f1b1ab816d9bd4629d4dbb7b8c7)) * Fine tuning marker comparator: Prefer the rightmost filepart when same matches count. ([`a8bbc5e`](https://github.com/guessit-io/guessit/commit/a8bbc5e2242f9dcb2a212cf6a599e8aef1f96e80)) * Merge pull request #490 from guessit-io/fix-travis-pypy Attemp to fix failing pypy env after travis image upgrade ([`21b8358`](https://github.com/guessit-io/guessit/commit/21b8358d98a08bd816048870b9f3917031252873)) * Attemp to fix failing pypy env after travis image upgrage ([`cc666b1`](https://github.com/guessit-io/guessit/commit/cc666b135b144f5cc8c689de1f0786be61fd6147)) * Some more ([`c25bf0b`](https://github.com/guessit-io/guessit/commit/c25bf0b809e7206527e979e4097f97d2f1536dca)) * Not detecting YHOO ([`0ed89bb`](https://github.com/guessit-io/guessit/commit/0ed89bb4ae1b657b919a63b0a5761795308d4abe)) * Disable disable=too-many-statements ([`094003c`](https://github.com/guessit-io/guessit/commit/094003cee6c069157c6f36921bc853b326f6d231)) * Move some stream services tests to streaming_services.yaml + add more ([`1890705`](https://github.com/guessit-io/guessit/commit/189070563b9be2d45a418b3993a7108b994d5db5)) * This shouldn't match 'streaming_services' property ([`6f9f4b2`](https://github.com/guessit-io/guessit/commit/6f9f4b26b5ae4f2ce9ac662afe98b32c5796a0a9)) * Add missing stream services ([`68a568c`](https://github.com/guessit-io/guessit/commit/68a568c24169ded5cb7a0d73ba40b19bedcccdca)) * Merge pull request #478 from fernandog/feature/stream_service_it Detect iT as iTunes stream service ([`ded8697`](https://github.com/guessit-io/guessit/commit/ded8697e038ceb4a842e82c4d63798ba76782df4)) * Updating docs and changelog ([`78ac41f`](https://github.com/guessit-io/guessit/commit/78ac41fcdbbafbf4e94475f36a7547b306d02c39)) * Fix for #276: Added aspect_ratio and validation for standard resolutions ([`cb3892b`](https://github.com/guessit-io/guessit/commit/cb3892bd113050d2d88504906fde070cd11567d1)) * Remove duplicate ([`8d2cfaa`](https://github.com/guessit-io/guessit/commit/8d2cfaa86454f02cb99889ac041936c8ec29f1aa)) * Detect iT as iTunes ([`6fe4c4b`](https://github.com/guessit-io/guessit/commit/6fe4c4b6931b2c13c7201f6fd3bb1686b630f4b0)) * Merge pull request #475 from guessit-io/feature/additional-audio-detection Enhance audio detection ([`b73d30e`](https://github.com/guessit-io/guessit/commit/b73d30e631203bd9f4174c0256a0ed01104c5331)) * Fix duplicated values in HISTORY.rst ([`102708e`](https://github.com/guessit-io/guessit/commit/102708e88bb1de1a5e4343947d5bd34f31d895d9)) * Merge pull request #474 from guessit-io/feature/edition-and-other-additions Add IMAX and Upscaled detections ([`08bdc0e`](https://github.com/guessit-io/guessit/commit/08bdc0ee152e5b849b4d8cb9998438b992d60f25)) * #471: Detect Opus audio codec ([`9447b9a`](https://github.com/guessit-io/guessit/commit/9447b9ac9f0f9ef30ee2d41943a51d5c8178cd39)) * #471: Detect Dolby Digital EX ([`c19e854`](https://github.com/guessit-io/guessit/commit/c19e85427e2d975db00a8ea82c427301cca3673b)) * #471: Detect DTS-ES ([`b18588e`](https://github.com/guessit-io/guessit/commit/b18588e9d4b36a01b164a56797201e15509dbd21)) * #471: Detect DTS-HD HRA ([`bf38978`](https://github.com/guessit-io/guessit/commit/bf3897888140d834ccf73c51a4bc565724bfa19c)) * Adding Fan edition ([`6d63341`](https://github.com/guessit-io/guessit/commit/6d63341e557caffa0af47ce3b1e43ed2d98f256a)) * Adding Ultimate edition ([`b1bd480`](https://github.com/guessit-io/guessit/commit/b1bd4801ad7f49868b08a44b723162018825b2ef)) * #470: Added Upscaled as other property ([`1a613e1`](https://github.com/guessit-io/guessit/commit/1a613e1e89ae16e858ad60b49a835ddef804456e)) * #470: Added new edition: IMAX ([`5041233`](https://github.com/guessit-io/guessit/commit/5041233a84af6582498068049e23ddc27d7c8774)) * Merge pull request #473 from guessit-io/feature/media-profile HDR / SDR and color space detection ([`c8d3fac`](https://github.com/guessit-io/guessit/commit/c8d3faceb8e5673b6384bdfdb130040a897c632a)) * Merge pull request #472 from guessit-io/feature/uhd-bluray Detect Ultra HD Bluray ([`35788b6`](https://github.com/guessit-io/guessit/commit/35788b69d62d6c25a0204947bf381d55608e9fb8)) * Fix for #468: Added SDR, HDR10, Dolby Vision, 12-bit and BT.2020 detection ([`684c7c5`](https://github.com/guessit-io/guessit/commit/684c7c57806a619eeb43aedc65b6f51cbbbcea20)) * #469: Detect Ultra HD Bluray ([`8fd004b`](https://github.com/guessit-io/guessit/commit/8fd004b51aa506903bda14c4a562f81691fdaf25)) * Merge pull request #464 from guessit-io/feature/properties-values Proposal for property values ([`2013f34`](https://github.com/guessit-io/guessit/commit/2013f34779f83cfaf469ebd854c2823d13633a11)) * Merge pull request #452 from guessit-io/feature/source Proposal for #365: Deprecate 'format' in favor of 'source' property ([`bba7a2b`](https://github.com/guessit-io/guessit/commit/bba7a2bbb407b36b9ca1d86339307759e76dd495)) * Update documentation ([`3451188`](https://github.com/guessit-io/guessit/commit/34511884f2ff9151d3e71b734fe2a6bb0b39e51b)) * Move 8bit/10bit from 'video_profile' to 'color_depth' ([`834e2fe`](https://github.com/guessit-io/guessit/commit/834e2feff24a7b9344c5bc92c1bda8016aa37fec)) * Refactored 'audio_codec' values ([`7bbbf76`](https://github.com/guessit-io/guessit/commit/7bbbf768e1adf9ed94948376b2cf82add71f0666)) * Fix wrong documentation about 'audio_codec: FLAC' ([`039191f`](https://github.com/guessit-io/guessit/commit/039191fbeef4d95658d2012ddc84c92c419c7b9d)) * Refactored 'video_codec' values ([`80cd9e1`](https://github.com/guessit-io/guessit/commit/80cd9e1c44301e0f6a07cf382de237ec90ceaa33)) * Refactored 'audio_profile' values. Added 'audio_codec: DTS-HD' ([`b9d6626`](https://github.com/guessit-io/guessit/commit/b9d6626b8a318628acd9b9dd704029e9f7c9da30)) * Refactored 'video_profile' values ([`a2893bb`](https://github.com/guessit-io/guessit/commit/a2893bb01e14d387855193ec513cf0c22581804d)) * Detecting interlaced resolutions. Changed '4K' to '2160p'. Added '4320p'. ([`cf8d4a3`](https://github.com/guessit-io/guessit/commit/cf8d4a3e8c3f36db2f7a257e680df94c05c14148)) * Refactored 'other' values as discussed in #459 ([`c3ce9b4`](https://github.com/guessit-io/guessit/commit/c3ce9b4ac73462a1c0e74ccdecddcea278de32f6)) * Moved 'other: FINAL' to 'episode_details: Final' ([`0b2bb1b`](https://github.com/guessit-io/guessit/commit/0b2bb1b43e6e219e310545534c813c311fb1595a)) * Changed 'episode_details: Omake' to 'episode_details: Extras' ([`f71d7c2`](https://github.com/guessit-io/guessit/commit/f71d7c2b6a0d7bbb5f1e6d9a15862ddf575fe602)) * Moved 'Ova' and 'Oav' from 'episode_details' to 'other' ([`fbdb17f`](https://github.com/guessit-io/guessit/commit/fbdb17fde310d02a7bae07cd4cc424b9f936d7e9)) * Moved CC and DDC from 'other' to 'edition' ([`62d8d60`](https://github.com/guessit-io/guessit/commit/62d8d606e12a1ce7befee1313509b6918a6ead9b)) * Dropped 'Edition' suffix from 'edition' values ([`50f8f44`](https://github.com/guessit-io/guessit/commit/50f8f446ea7b46f0316f6439a05ab378c9e63068)) * Merge remote-tracking branch 'origin/develop' into feature/source ([`fdcc531`](https://github.com/guessit-io/guessit/commit/fdcc53145c3b81f4d6cef0878f2b7d1b1663bd27)) * Merge pull request #461 from guessit-io/feature/priority-validate-neighbor 'other' is detected with no neighbors ([`183f190`](https://github.com/guessit-io/guessit/commit/183f19025cd833223b1d4322d662af1994776598)) * Fix for #460: 'other' is detected with no neighbors ([`9e5fc60`](https://github.com/guessit-io/guessit/commit/9e5fc600901c7d0a5bab3fd83b4e065d656b3d5b)) * Adding migration instructions from 2.x to 3.x ([`4d6767d`](https://github.com/guessit-io/guessit/commit/4d6767de46e236f24991dcea5a04f8e7baf8ff08)) * Updating docs ([`929c9c7`](https://github.com/guessit-io/guessit/commit/929c9c7c38c53ba6eb8005ead5bc23fbacafcb26)) * Rename WEB to Web ([`38d7371`](https://github.com/guessit-io/guessit/commit/38d737130ffff16b96fb74be845e94fc0f75b9cb)) * Merge remote-tracking branch 'origin/develop' into feature/source ([`a6965b0`](https://github.com/guessit-io/guessit/commit/a6965b08f01c1165e0acafe13195f4cf3aae576f)) * Back to development: 3.0.0 ([`ac2de2d`](https://github.com/guessit-io/guessit/commit/ac2de2de05e893d9c45535afa0b2dfeb7629f152)) * Preparing release 2.1.4 ([`b2ef20c`](https://github.com/guessit-io/guessit/commit/b2ef20cb760c20f019f7280049f686bf1c653ad5)) * Update history ([`93f58cd`](https://github.com/guessit-io/guessit/commit/93f58cd3d5ebbe402d3916441fac9c59c0a14a9d)) * Fix failing test ([`2d02306`](https://github.com/guessit-io/guessit/commit/2d02306d1e81dce5fa4dbb2f91557b2b697eea9d)) * Removing 'format' property. Renaming BluRay to Blu-ray ([`68548c9`](https://github.com/guessit-io/guessit/commit/68548c91f406f4c0318ad00f55862309b65568e6)) * Add HD Camera/Telecine/Telesync. Rename PPV to Pay-per-view. Fix wrong HDRip detection. WEBCap is a synonym to WEBRip ([`03d88a9`](https://github.com/guessit-io/guessit/commit/03d88a9f96063b2899b446569fc94697e50dc5f2)) * Fix for #365: Deprecate 'format' property in favor of 'source' ([`2bfac49`](https://github.com/guessit-io/guessit/commit/2bfac49c6ef33db4da4bf172548da41ad8f3ac78)) * Renamed 'format' python module to 'source' ([`83187b0`](https://github.com/guessit-io/guessit/commit/83187b0e4276590a2f500504ecb0c9a837796ef2)) * Fix new options docs ([`53db177`](https://github.com/guessit-io/guessit/commit/53db177a51935c3860822955e19339878f35b5bd)) * Merge pull request #455 from guessit-io/feature/rebulk-0.9 Make returned dict structure consistent between CLI and API ([`432f2c8`](https://github.com/guessit-io/guessit/commit/432f2c840e4007c4f0a6c159d6f85a708bc81d58)) * Make returned dict structure consistent between CLI and API This adds --enforce-list and --single-value options to CLI (enforce_list and single_value to API). Close #274 Close #453 ([`dca90cb`](https://github.com/guessit-io/guessit/commit/dca90cbba956a45207776840b7bd33461eb4e25b)) * Merge pull request #451 from guessit-io/feature/edition Move edition values from 'other' to 'edition' property ([`6b25b03`](https://github.com/guessit-io/guessit/commit/6b25b03a5fd703707c6687af7cfcc7aba55ec255)) * Rename DolbyDigital to AC3 # Conflicts: # docs/properties.rst ([`9362a45`](https://github.com/guessit-io/guessit/commit/9362a45064bd85cbda0b362170557543f91dfcbe)) * Fix missing coma Close #454 ([`0270507`](https://github.com/guessit-io/guessit/commit/0270507394d9f3d8f1be310d38f0a498b7e668f6)) * Back to development: 3.0.0 ([`39923b9`](https://github.com/guessit-io/guessit/commit/39923b9a9a7b5a597119b47e9c7eed75b5eb49cb)) * Preparing release 2.1.3 ([`e61659f`](https://github.com/guessit-io/guessit/commit/e61659f856eb6404b825a6793f71bf25a5333f29)) * Fix for #421: Move edition values from 'other' to 'edition' property ([`f7c992d`](https://github.com/guessit-io/guessit/commit/f7c992db4a234acc37495c73b47180c55f15c768)) * Merge pull request #450 from guessit-io/feature/python-3.6 Adding python 3.6 support ([`44ce700`](https://github.com/guessit-io/guessit/commit/44ce700ce349c62aca2734927e6753c251fe76ea)) * Updating history with python 3.6 support ([`d720086`](https://github.com/guessit-io/guessit/commit/d720086ab36222a085978f15f627734c4f8bae86)) * Adding python 3.6 support ([`d664d23`](https://github.com/guessit-io/guessit/commit/d664d23f01bc366c57a0a7653a843abd8fa17881)) * Merge pull request #449 from guessit-io/feature/other-fullhd Detecting FullHD ([`3bbb5f9`](https://github.com/guessit-io/guessit/commit/3bbb5f9f9a6fcf112a90a3ce2831fd01ce3ec58d)) * Updating history ([`38aab7d`](https://github.com/guessit-io/guessit/commit/38aab7d0c49ef091e3068c525ad6812d79699f8a)) * Fix for #432: Detecting FullHD ([`1d331b8`](https://github.com/guessit-io/guessit/commit/1d331b84ae5ee5414328ead11196505ac661e7ad)) * Merge pull request #448 from guessit-io/feature/update-properties-docs Updating properties documentation ([`bcd18d7`](https://github.com/guessit-io/guessit/commit/bcd18d71155dfbab95687abdeb148437e0359348)) * Updating properties.rst with missing values. Fixing rst format issues. Ordering properties values ([`6c73063`](https://github.com/guessit-io/guessit/commit/6c7306302c643b32aa49d3beb08caf4e2e4d592e)) * Fix for #330: Rename DolbyDigital to AC3 ([`075538e`](https://github.com/guessit-io/guessit/commit/075538ed8ec0ad6d95fd0395ae971cd1c6236eaf)) * Merge pull request #446 from guessit-io/feature/enhance-weak-movie-removal-logic Fix `episode` type detection when series name contains `year` followed by SEE pattern ([`d96859d`](https://github.com/guessit-io/guessit/commit/d96859d056864b8956cbeb8c8f5bb6875d270e39)) * Add test scenario for #428 ([`eba607d`](https://github.com/guessit-io/guessit/commit/eba607d4559ef98fe1c679973add7853d8953d77)) * Fix for #235 and #428: Do not remove 'weak-movie' after 'year' ([`72bd07f`](https://github.com/guessit-io/guessit/commit/72bd07f33561c4d0413be07e8da7b9821c01679a)) * Merge pull request #445 from guessit-io/feature/eac3-audio-codec EAC3 as audio_codec ([`d1c4841`](https://github.com/guessit-io/guessit/commit/d1c48411c063c80a1fe019e44355eaf1121902d1)) * Adding EAC3 to audio_codec documentation ([`ae9a5a1`](https://github.com/guessit-io/guessit/commit/ae9a5a107ec4bdf37e6b6e8de238b4104d80f131)) * Fixes #438 and ##431: EAC3 as audio_codec ([`416aa0e`](https://github.com/guessit-io/guessit/commit/416aa0e445f25bc90bacb523c0201745b0e99d65)) * Merge pull request #444 from guessit-io/feature/enhance-container-detection Enhance container detection ([`67164cf`](https://github.com/guessit-io/guessit/commit/67164cf967eed46dcdd6c4f9adaa95028bdf7a07)) * Updating changelog ([`fa24901`](https://github.com/guessit-io/guessit/commit/fa249017c27dbe891dc3bc567d38e5f675989575)) * Fixes #422: container values should be lowercase (as per documentation) ([`d1e8b06`](https://github.com/guessit-io/guessit/commit/d1e8b06a2b10b5187225d5be9eea6602d050dba8)) * Updating container docs ([`3e25396`](https://github.com/guessit-io/guessit/commit/3e2539658e3cdadbd958149b53a4952a34ba941d)) * Fix for #346: detect nzb as container ([`831fa87`](https://github.com/guessit-io/guessit/commit/831fa876a828fa176847daaf81ec5ffd1018a695)) * Merge pull request #443 from guessit-io/feature/enhance-episode-title-detection 'year' and 'part' shouldn't be detected when in 'episode_title' location ([`8d56c9f`](https://github.com/guessit-io/guessit/commit/8d56c9f23ceae87221d50de644c8e0c66176dc26)) * Updating changelog ([`d93705a`](https://github.com/guessit-io/guessit/commit/d93705aad60a4921b7323cbf1e717046345a9907)) * Fixes #331, #435 and #440: 'year' and 'part' shouldn't be detected when in 'episode_title' location ([`09e9246`](https://github.com/guessit-io/guessit/commit/09e9246cb94f1094e4b5ca68939d789f9cd999c1)) * Merge pull request #442 from guessit-io/feature/fix-unknown-lang-detection Fix for #441: Unknown shouldn't be detected as language ([`268167b`](https://github.com/guessit-io/guessit/commit/268167b98ded7d23172d755581ea7c79c4b4e780)) * Dropping python 2.6 support ([`f630d85`](https://github.com/guessit-io/guessit/commit/f630d850d08bc2860acc3331266e951952e2350f)) * Fix for #441: Unknown shouldn't be detected as language ([`066de49`](https://github.com/guessit-io/guessit/commit/066de49184f87a4ee1a33dc075ad19a84d2a520e)) * Merge pull request #437 from guessit-io/feature/fix-disable-weak-episode-if-movie Disable weak episode if type is movie ([`b4ed4b5`](https://github.com/guessit-io/guessit/commit/b4ed4b57e1e66e2862e8f87053eea3a2fcab33db)) * Fixes #429: Disable weak episode if type is movie ([`ac0a66f`](https://github.com/guessit-io/guessit/commit/ac0a66f37ad612ca0fae99a209ff93ed8636cf08)) * Merge pull request #436 from guessit-io/feature/fix-wrong-audio-detection Invalid audio detection ([`ad775aa`](https://github.com/guessit-io/guessit/commit/ad775aa93d34f5907386692cbdd7933eae1416ca)) * Ignoring pylint false positives ([`6d6842d`](https://github.com/guessit-io/guessit/commit/6d6842d3ff6463c620f7bbd6e98e16df0359ef8f)) * Fixing pylint issues ([`3d61863`](https://github.com/guessit-io/guessit/commit/3d61863696ae27251822ee11855a1c21ba0c1c13)) * Fixes #434: Invalid audio detection ([`cc706e2`](https://github.com/guessit-io/guessit/commit/cc706e24ef125fa35fcf7e9a3db9c979d8ef564a)) * Back to development: 2.1.3 ([`dc1f017`](https://github.com/guessit-io/guessit/commit/dc1f0179495f302e39d9b2e1dbddce0cffa04792)) * Preparing release 2.1.2 ([`4c5114a`](https://github.com/guessit-io/guessit/commit/4c5114a109185129c482e559e5b6e3f35ff72821)) * Update manifest with configuration files ([`084875e`](https://github.com/guessit-io/guessit/commit/084875e27e3f837e9938fbd48670374e9ba19f43)) * Update changelog ([`9c5cc3c`](https://github.com/guessit-io/guessit/commit/9c5cc3cd7666f2a7ea643d4932191d1f63d9254f)) * Merge pull request #420 from guessit-io/feature/reserved-words Detecting tags/reserved words ([`db8e3fc`](https://github.com/guessit-io/guessit/commit/db8e3fc19584c9eab28e0e36f034a23c1ac1d274)) * Detecting WEST.FEED and EAST.FEED ([`70900a4`](https://github.com/guessit-io/guessit/commit/70900a4a45c89f687ec2af59291ab65c2850b9df)) * Enhancing Special Edition detection ([`a2fb629`](https://github.com/guessit-io/guessit/commit/a2fb6292e92386072c342a7a227222661d50edae)) * Detecting OAR ([`01b6ad5`](https://github.com/guessit-io/guessit/commit/01b6ad58db189903c9594ef1f7fc3b73553523ca)) * Detecting LIMITED ([`d980f97`](https://github.com/guessit-io/guessit/commit/d980f9710d97c64c540fe73a08daa383c1a33b62)) * Fix STV detection - Straight to Video ([`fe7bed0`](https://github.com/guessit-io/guessit/commit/fe7bed00e9cf10de644e935cbb070c5ab7ee5273)) * Adding UNCUT test scenario ([`24dc237`](https://github.com/guessit-io/guessit/commit/24dc2373114f16047c7e2e08847266531aee0c38)) * Detecting UNCENSORED ([`01cf05d`](https://github.com/guessit-io/guessit/commit/01cf05d7088aaf341759ecda89a2922cadcc2c3c)) * Detecting Theatrical Edition ([`e8c2b8f`](https://github.com/guessit-io/guessit/commit/e8c2b8fcd4702f5e36458114d3627bdbf1d08c66)) * Detecting SAMPLEFIX ([`39a0459`](https://github.com/guessit-io/guessit/commit/39a04597f0d5c3678eee8555d43bd4d320ad9abc)) * Detecting READNFO ([`d678e4a`](https://github.com/guessit-io/guessit/commit/d678e4a57dc2e1e9c734ec8b02656e3ac8d7017a)) * Detecting PROOFFIX ([`1cc067f`](https://github.com/guessit-io/guessit/commit/1cc067fe5dff09fcebe651c2eaa64d1a72b1c067)) * Detecting Open Matte ([`3892b73`](https://github.com/guessit-io/guessit/commit/3892b73e60ccbbbdf3320377c5b9888ee64f3e4c)) * Detecting NFOFIX ([`9cf2655`](https://github.com/guessit-io/guessit/commit/9cf2655eec9e4a01123935e818597cb04407a158)) * Detecting INTERNAL ([`f716835`](https://github.com/guessit-io/guessit/commit/f7168351ef3200f71e86158edd0ee3bc147f3208)) * Updating FINAL documentation ([`8bf5c4e`](https://github.com/guessit-io/guessit/commit/8bf5c4eab3043e3c9873a4a9c6f08417ce592cd9)) * Detecting FESTIVAL ([`660c99f`](https://github.com/guessit-io/guessit/commit/660c99f12f77a186add402bdfca8cacfe24e8ac5)) * Adding DUB test scenario ([`788d4b4`](https://github.com/guessit-io/guessit/commit/788d4b4bd0b9de5dd1c2a5094aa772bc6a5fb459)) * Detecting Documentary ([`2426998`](https://github.com/guessit-io/guessit/commit/24269985a1b0eaf3d845817a1008fbac78e12951)) * Detecting DIRFIX ([`5a484d2`](https://github.com/guessit-io/guessit/commit/5a484d29213230ed14266ac08c126b6e908184d7)) * Enhancing Director's cut detection ([`caa6b76`](https://github.com/guessit-io/guessit/commit/caa6b7656279b6c3e691f5624dde5cac0883b3d7)) * Detecting COLORIZED ([`239383f`](https://github.com/guessit-io/guessit/commit/239383fe7a752b3796c67dca28e715a62919d048)) * Detecting CONVERT ([`7721986`](https://github.com/guessit-io/guessit/commit/7721986d94ffbe7821d1188a13ebeb5f6953e613)) * Detecting season in dutch ([`2c1df3a`](https://github.com/guessit-io/guessit/commit/2c1df3ab813f956baf546309efca53a61e86b40b)) * Detecting ALTERNATIVE CUT ([`a89c98e`](https://github.com/guessit-io/guessit/commit/a89c98ee42612ef8741aa93352865ebc3a9a523a)) * Merge pull request #419 from guessit-io/feature/detection-improvements Detection improvements: format, video_profile, audio_channels, streaming_service, UltraHD ([`dc05118`](https://github.com/guessit-io/guessit/commit/dc0511868eac576b611ba6ec87a9767aa383bca8)) * Minor performance improvement ([`715c0f6`](https://github.com/guessit-io/guessit/commit/715c0f60feefd3f26485c9325ee2917b81397280)) * Proper audio_channels detection and validation ([`c4184af`](https://github.com/guessit-io/guessit/commit/c4184af1e7e47aaf938295abad24c61237725e86)) * Fixing audio_channels detection ([`f2a460f`](https://github.com/guessit-io/guessit/commit/f2a460f6731612ad0bbec7c71216fb60b6e3da98)) * Fix indentation issue ([`098179b`](https://github.com/guessit-io/guessit/commit/098179b60b7baca0be989c9b91edc3a567ffec47)) * Updating docs ([`047279f`](https://github.com/guessit-io/guessit/commit/047279fa656d098dc987516dcec02cb4a1a14f9c)) * Fixing lint issues ([`3f72b09`](https://github.com/guessit-io/guessit/commit/3f72b0922b3aeccc14abfc6576904e267777f646)) * Enhancing streaming_service detection/other without separators. Added several test scenarios ([`ee0ee54`](https://github.com/guessit-io/guessit/commit/ee0ee542ef0481837815497037e6d11596607275)) * Enhancing video_profile detection ([`cc113db`](https://github.com/guessit-io/guessit/commit/cc113db2fb81171ac7fea81f787d336f6cc408e6)) * Enhancing audio_channels detection ([`d1ef82c`](https://github.com/guessit-io/guessit/commit/d1ef82c8c4bfbc057d3048ab991c8522b3e14127)) * UHD 'other' detection ([`5c2a797`](https://github.com/guessit-io/guessit/commit/5c2a79719d9cee94d9140763777efcc8913f7031)) * UHDTV format detection ([`ac054b4`](https://github.com/guessit-io/guessit/commit/ac054b4bbfd534f2f84952b1abf4daea56415580)) * Merge pull request #418 from guessit-io/feature/screen-size-conflicts Remove season/episode matches which conflicts with screen_size ([`6c8159f`](https://github.com/guessit-io/guessit/commit/6c8159f2f633a9baddd9f419a264dcaeca2af933)) * Enhancing screen_size conflict detection ([`ae6e906`](https://github.com/guessit-io/guessit/commit/ae6e9065e391051ec901d3b6fa8336f46d3a6ed2)) * Enhancing TV format detection and dubbed detection ([`32d98cf`](https://github.com/guessit-io/guessit/commit/32d98cf0bfb4bbadb413826a4d2c1205b136eae9)) * Minor enhancement in video_profile detection ([`49c9b8e`](https://github.com/guessit-io/guessit/commit/49c9b8e6508a47112035c06136c64f0f48e059f2)) * Fix for #308: Remove season/episode matches which conflicts with screen_size ([`e39c4d2`](https://github.com/guessit-io/guessit/commit/e39c4d2f3e0686f8c0cfabb92e832f0e70e29aa7)) * Merge pull request #416 from guessit-io/feature/remove-invalid-season-episode-matches Discarding invalid season/episode matches ([`9b2e0a4`](https://github.com/guessit-io/guessit/commit/9b2e0a42d4ee5876a8146471fca4c4f19edc4a32)) * Fix for #415, #383, #352: Discarding invalid season/episode matches ([`8a62d4c`](https://github.com/guessit-io/guessit/commit/8a62d4cff961fe635f1df5b4cab39676c6c0b44e)) * Merge pull request #412 from guessit-io/feature/enhance-language-support Fix for #321: Enhancing language support ([`89a01b0`](https://github.com/guessit-io/guessit/commit/89a01b012403a4dab5bc928b47878bd4a085e3a5)) * Fix for #321: Enhancing language support ([`ab60717`](https://github.com/guessit-io/guessit/commit/ab6071757ffd532fd336c15bed4cfa92a700f2f2)) * Merge pull request #414 from guessit-io/feature/title-episode_details-conflict episode_details should be removed when conflicting with title ([`d317553`](https://github.com/guessit-io/guessit/commit/d317553f02b327b6cbf837ff1d3941b4d0a38192)) * Fix for #413: episode_details should be removed when conflicting with title ([`2aa362f`](https://github.com/guessit-io/guessit/commit/2aa362f3e3a307fb47f4d5dd837f291965f04642)) * Merge pull request #404 from guessit-io/feature/fix-ofwords-regex Fix for detached episode/season count regex ([`e94af2b`](https://github.com/guessit-io/guessit/commit/e94af2bf437cc0e672f16f5b192b633442408cf9)) * Merge branch 'develop' into feature/fix-ofwords-regex ([`3d1e767`](https://github.com/guessit-io/guessit/commit/3d1e76760246f9981fe3fe78759aed7aeba718ed)) * Merge pull request #407 from guessit-io/feature/hardcoded-subs Detect hardcoded subtitles ([`d0f1183`](https://github.com/guessit-io/guessit/commit/d0f11835df327c24b0db45aad04ccd03a07ddbc9)) * Merge branch 'develop' into feature/hardcoded-subs ([`55b3948`](https://github.com/guessit-io/guessit/commit/55b394831159a22b2a1463a8ac2a22795c8a8c81)) * Merge pull request #403 from guessit-io/feature/website-prefixes Handling website prefixes ([`a6bbd83`](https://github.com/guessit-io/guessit/commit/a6bbd838afa479eb6ca2ac55603ea92cf61ef1cd)) * Merge branch 'develop' into feature/website-prefixes ([`bfaad93`](https://github.com/guessit-io/guessit/commit/bfaad93b501cbe7cfb33bd458ca8d308a6151970)) * Merge pull request #402 from guessit-io/feature/enhance-filepart-2-episode-title Enhances Filepart2EpisodeTitle logic to properly guess episode_title ([`8b5df70`](https://github.com/guessit-io/guessit/commit/8b5df70f9de510b7a023f8eeb6127bc251521752)) * Merge branch 'develop' into feature/enhance-filepart-2-episode-title ([`0e36413`](https://github.com/guessit-io/guessit/commit/0e364133757bba0518c3ed82d71e46063822856c)) * Merge pull request #405 from guessit-io/feature/prefer-sxxexx SxxExx pattern should be preferred over weak matches ([`6a850b3`](https://github.com/guessit-io/guessit/commit/6a850b309b031e9b32634544434130041aa73b1d)) * Don't parse options from process arguments when using guessit API ([`3fd27df`](https://github.com/guessit-io/guessit/commit/3fd27df36fc7ce94753ef12d8fc4c139866664a0)) * Pylint fixes ([`d0c845b`](https://github.com/guessit-io/guessit/commit/d0c845b91f4071e1618f3eebd4cd21b24f25c28c)) * Detect hardcoded subtitles. Fix for #318 ([`36abccf`](https://github.com/guessit-io/guessit/commit/36abccfea9c72940643fe21c029a3886ab3ab9f4)) * Regression tests for #394 and #399 ([`ad068a4`](https://github.com/guessit-io/guessit/commit/ad068a452b4e64af4620e0be2c1654347e1b3136)) * Pylint fixes ([`3101a07`](https://github.com/guessit-io/guessit/commit/3101a073c4822e901ea2d0444d7387dd07548f66)) * Regression tests for #398 ([`604f24a`](https://github.com/guessit-io/guessit/commit/604f24a5aea90c3d45bf8978e4f0f59aac1a22ef)) * Fixes #396: SxxExx pattern should be preferred over weak matches ([`30afedf`](https://github.com/guessit-io/guessit/commit/30afedff7d0e94b8d2b57cb09b81dead6256dd54)) * Fixes #400: Minor issue in regex ([`df57a9e`](https://github.com/guessit-io/guessit/commit/df57a9e731211b3860d1266838cb5de410662c9b)) * Fixes #392: Handling website prefixes ([`2505640`](https://github.com/guessit-io/guessit/commit/2505640bf172c30ef1b76d506cb37345c7d15171)) * Fixes #393: Enhances Filepart2EpisodeTitle logic to properly guess episode_title ([`8caa3cf`](https://github.com/guessit-io/guessit/commit/8caa3cfb7e271443e72c4980f6d06f60445c6bca)) * Merge branch 'patch-1' into develop ([`1a9cf06`](https://github.com/guessit-io/guessit/commit/1a9cf06af6b406d09be1de7d8dd840795063d73d)) * Add additional Streaming Services ([`62c8194`](https://github.com/guessit-io/guessit/commit/62c8194c83573f75edf7bbf31b2755a8b25743e0)) * Fix testing issue on python 2 when input string is not unicode but some values are ([`b8520f1`](https://github.com/guessit-io/guessit/commit/b8520f1dad58e30a4ff9e6cff99c8f080887fe70)) * Merge pull request #382 from guessit-io/feature/default-configuration Add default configuration ([`d51cf8a`](https://github.com/guessit-io/guessit/commit/d51cf8a1b81a651e5d68f37001d9d9931b46f195)) * Add default configuration with OSS 117 ([`1bcbc7a`](https://github.com/guessit-io/guessit/commit/1bcbc7a5e7e26ea1584d04f84e9560cc2df6bd3e)) * Merge pull request #386 from guessit-io/feature/enhance-clean-groupname Do not strip out parts of release groups ([`7dc9be4`](https://github.com/guessit-io/guessit/commit/7dc9be483b38bdc2b4222865e51e6308be120d59)) * Merge pull request #387 from guessit-io/feature/handle-empty-groups Handling empty groups ([`2cdc836`](https://github.com/guessit-io/guessit/commit/2cdc8367bf959aadd04ffbeb610490c5448f8945)) * Closes #385: Handle empty groups ([`9569538`](https://github.com/guessit-io/guessit/commit/956953878e773b8f4e8cede272725c2ddc9d3c95)) * Closes #297: Do not strip out parts of release groups. ([`b11108a`](https://github.com/guessit-io/guessit/commit/b11108ab8c3b1fc9edcf4004c008b3de1070c65e)) * Update usage in README ([`acfbf08`](https://github.com/guessit-io/guessit/commit/acfbf0805bf2619048a7c7a5dbb4155072257776)) * Merge pull request #380 from guessit-io/feature/configuration-file Add configuration file feature ([`7a42a25`](https://github.com/guessit-io/guessit/commit/7a42a2555f55236b74a4161055000e3e5828d334)) * Add configuration file feature Close #374 ([`1f7b1f0`](https://github.com/guessit-io/guessit/commit/1f7b1f070e1e3f06de70ac29104b6cc397a36fd2)) * Merge branch 'feature/expected' into develop # Conflicts: # guessit/test/episodes.yml ([`4262ade`](https://github.com/guessit-io/guessit/commit/4262ade6a373aa3d4191bb4ae82e2851283e2f74)) * Merge pull request #379 from guessit-io/feature/fix-377 Fix for #377 ([`9b53cdb`](https://github.com/guessit-io/guessit/commit/9b53cdb620fa161e7689d31103a48b75903afa4f)) * Ensure expected_title and expected_group is surrounded with separators ([`7634f7f`](https://github.com/guessit-io/guessit/commit/7634f7fbaecb1f376bfea5eb5edecacae4fc2862)) * Use search input as value for expected properties ([`2c7c631`](https://github.com/guessit-io/guessit/commit/2c7c6317e5187656589feec99ff9c175d12c08ea)) * Fix episode and year conflict ([`5f47363`](https://github.com/guessit-io/guessit/commit/5f473638c14f3d8e70b4395d00f4b0af439810fd)) * Disable RemoveWeakIfMovie rule when context type=episode Close #377 ([`491a732`](https://github.com/guessit-io/guessit/commit/491a732a26300c97ddf6052a7c02707971191bfa)) * Enhance expected_title and expected_group Close #376 ([`b720697`](https://github.com/guessit-io/guessit/commit/b720697f556d148210d75f728ea307d563a19fe5)) * Merge pull request #375 from guessit-io/feature/mux Implement mux detection ([`527c0ab`](https://github.com/guessit-io/guessit/commit/527c0ab00f7f35b2eb6cd062a3b59a60e83d8d7f)) * Updating docs ([`060897c`](https://github.com/guessit-io/guessit/commit/060897c258d8f2967a811ba58a2946285d950027)) * Relative imports should be used ([`f18f00b`](https://github.com/guessit-io/guessit/commit/f18f00b0bacc94b44236f5b5c652199c322b2494)) * Detect AVC as h264 video codec ([`73d48d0`](https://github.com/guessit-io/guessit/commit/73d48d09053c2db977483b8c6141f59ca00f0e43)) * Mux detection. Closes #307 ([`cacda62`](https://github.com/guessit-io/guessit/commit/cacda62f63e082a948164997e54b0da7d9261599)) * Added ReEncoded to other # Conflicts: # guessit/test/episodes.yml ([`20c4cec`](https://github.com/guessit-io/guessit/commit/20c4cecdbf0b97450bddb54b23510bd2cf6f9f4d)) * Merge pull request #372 from guessit-io/feature/size Added size property ([`6c8746c`](https://github.com/guessit-io/guessit/commit/6c8746c6498b12918379b5eb0580edacff2e72a2)) * Add missing docstring ([`f82706e`](https://github.com/guessit-io/guessit/commit/f82706e5d393f2c0af3a3b829288540ec2da01a7)) * Fixing imports in size.py ([`8021531`](https://github.com/guessit-io/guessit/commit/80215312ae813ac92d55bd85678364d373a04ae3)) * Updating properties docs ([`3fe136e`](https://github.com/guessit-io/guessit/commit/3fe136ef07218fd9faae974d2313db3d35a90036)) * Adding size detection. Closes #299 ([`f7db0eb`](https://github.com/guessit-io/guessit/commit/f7db0eb427d70053072b459ae40593ee155c7004)) * Updating properties docs ([`644c8b8`](https://github.com/guessit-io/guessit/commit/644c8b8a8663920b2d7cf3614c6416287025b1d6)) * Implementing Re-Encoded guessing. Closes #300 ([`6fe63d2`](https://github.com/guessit-io/guessit/commit/6fe63d22b07c6ec5ab3d6d6cf853a43861dd7b35)) * Docker images are now available in guessit organization ([`a8a80cc`](https://github.com/guessit-io/guessit/commit/a8a80cc69c3b645fda5c6e1e5e6d9d291982bf9f)) * Back to development: 2.1.2 ([`fcd1747`](https://github.com/guessit-io/guessit/commit/fcd17476dd950e2386bf2cdfe0e773e91eae31f9)) * Preparing release 2.1.1 ([`039f0f5`](https://github.com/guessit-io/guessit/commit/039f0f5232fdd62c47234eae1bb207369a78f412)) * Update changelog ([`1df4099`](https://github.com/guessit-io/guessit/commit/1df4099c6feb4d16e2536c3411c69be285c85e7f)) * Merge pull request #370 from guessit-io/feature/strip-separators-processor Strip separators from matches ([`74b64d3`](https://github.com/guessit-io/guessit/commit/74b64d3edb35c13b72eb73d5c3d8cf53177a9c57)) * Strip separators from matches This is useful in advanced mode for raw values like title, episode_title and release_group to have separators stripped off. ([`605526e`](https://github.com/guessit-io/guessit/commit/605526e9858cc1cafc5aba75f2b077b212bd2a8b)) * Detect DDP (Dolby Digital Plus) as DolbyDigital ([`d434514`](https://github.com/guessit-io/guessit/commit/d434514588deb8c85773de89fb3ed91f5a19cc1e)) * Enhance guessit performance by using rebulk 0.8.2 ([`6dc8ece`](https://github.com/guessit-io/guessit/commit/6dc8ece541122cd0f1e8cf5c8c36e50cd3c3d8bd)) * Remove twine dependency constraint ([`1fcbe88`](https://github.com/guessit-io/guessit/commit/1fcbe883653b4f90c426cd90cc0041fe37472313)) * Upgrade to rebulk 0.8.1 to support python 2.6 ([`92bf41d`](https://github.com/guessit-io/guessit/commit/92bf41d449d9a0e5e7d5405f13e9e045001fd155)) * Fix for #357: Episode is not detected in a less relevant filepart # Conflicts: # guessit/test/episodes.yml ([`017649e`](https://github.com/guessit-io/guessit/commit/017649ea7387619503d354522582e08b24247994)) * Keep more specific season/episode # Conflicts: # guessit/test/episodes.yml ([`5e50449`](https://github.com/guessit-io/guessit/commit/5e504492b78b68a7b0541ab54e60b30fc2f8bfdb)) * Fix episode range starting from 1 prefixed by episodes word ([`b6b3a00`](https://github.com/guessit-io/guessit/commit/b6b3a00ea5b57a820065830071800d6282ca8e53)) * Detecting date followed by screen_size # Conflicts: # guessit/test/episodes.yml ([`742f6fa`](https://github.com/guessit-io/guessit/commit/742f6fa7fb4f0750d9df4255d8f0be0dc133c88e)) * Fix pylint issue ([`cb4a31e`](https://github.com/guessit-io/guessit/commit/cb4a31ed32f1e5ea28296553d55e6969803b8420)) * Upgrade to rebulk 0.8.0 and use chain_breaker to avoid generating too long ranges Close #353 ([`243f8f8`](https://github.com/guessit-io/guessit/commit/243f8f83fc402757dff97ec70d622a68bbadcb5d)) * Guess full name streaming services ([`efd37f7`](https://github.com/guessit-io/guessit/commit/efd37f7e7f3519ff00b34f8d37630978013968ad)) * Add streaming_service property to docs ([`d326689`](https://github.com/guessit-io/guessit/commit/d32668998a4045d41ce4a96ad5c0bb7dc5273126)) * Merge pull request #361 from ratoaq2/feature/add-streaming-service Adding streaming_service property ([`e84459a`](https://github.com/guessit-io/guessit/commit/e84459a94c68867594433dfcd8432e1d4c1dfab9)) * Upgrade to rebulk 0.7.7 to fix chain patterns in certain scenarios Close #359 ([`cbda121`](https://github.com/guessit-io/guessit/commit/cbda121ce7a310fcefbb61f73aeb85ed03db923e)) * Fix for #357 ([`ae7d133`](https://github.com/guessit-io/guessit/commit/ae7d1334f00bfa611b22a4713c8fc1e1dca4bc16)) * Fixes #360: Keep more specific season/episode ([`7ff4aca`](https://github.com/guessit-io/guessit/commit/7ff4acaa01baaac8bac6c7ad10a2181473ff2834)) * Merge pull request #364 from ratoaq2/feature/detect-more-formats More formats to be detected ([`d1dce4d`](https://github.com/guessit-io/guessit/commit/d1dce4d98273049cfd1815f03dfdb2214d7431a0)) * Merge pull request #363 from ratoaq2/feature/improve-video-profile-detection Improving video_profile detection ([`878d857`](https://github.com/guessit-io/guessit/commit/878d857c7d585692cf11590ca42af7f86b95ae61)) * Fixes #315: More formats to be detected ([`30ef63b`](https://github.com/guessit-io/guessit/commit/30ef63b4b3ff3d4b0bbc30625b2512d44994cc27)) * Fixes #350: improving video_profile detection ([`3c2269d`](https://github.com/guessit-io/guessit/commit/3c2269d56143a88e34cb237cc97107226af94427)) * Fix #351: detecting date followed by screen_size ([`56dc4f3`](https://github.com/guessit-io/guessit/commit/56dc4f3cfb513d3c3e0fd2a77c0ee69002773325)) * Adding streaming_service property ([`4744552`](https://github.com/guessit-io/guessit/commit/4744552f4f3ecaf4cf2ce58b992d0f288cd1bed1)) * Remove unused import ([`35e1ac6`](https://github.com/guessit-io/guessit/commit/35e1ac640023aa20fb3efae72e0ea0ca270a1307)) * Fix website validation Close #345 ([`54bed86`](https://github.com/guessit-io/guessit/commit/54bed865a6b1d82d16b0125b7d833177abace6f2)) * Prefer release group over second title inside filepart Close #343 ([`e760fe2`](https://github.com/guessit-io/guessit/commit/e760fe2ffe2dbf9a2f9f6aff9b28bd638643801a)) * Fix validation of film property Close #294 ([`98b0991`](https://github.com/guessit-io/guessit/commit/98b09911cd4f2bb42a02d7def383d91df80f261a)) * Enhance episode/season range and sequence guessing Close #311 ([`a4fb286`](https://github.com/guessit-io/guessit/commit/a4fb2865d4b697397aa976388bbd0edf558a24fb)) * Fix invalid comparator in audio_codec conflict solver ([`57fe78f`](https://github.com/guessit-io/guessit/commit/57fe78f9e609dfaa60c66432d21a4c0a1f9f11ba)) * Fix HDD release group detected as DolbyDigital Close #317 ([`232ef2b`](https://github.com/guessit-io/guessit/commit/232ef2b0beabe5c6d3f066918b674ca417570b88)) * Add name parameter to build_or_pattern re utils ([`c48edbd`](https://github.com/guessit-io/guessit/commit/c48edbd1c536d8d6a0622dd4bc914acc2cb36739)) * Ensure roman numbers are surrounded with separators Close #304 ([`07aeb52`](https://github.com/guessit-io/guessit/commit/07aeb527885728daefa5fb52d8684b8e5b24e224)) * Enhance season/episode guess validation ([`606fe40`](https://github.com/guessit-io/guessit/commit/606fe40fef127137c4d7638ca628a50e0fb144fd)) * Fix Title not properly guessed when 'Complete' appears with 'The' article. Requires new rebulk version (0.7.5) Close #340 ([`0a04fd3`](https://github.com/guessit-io/guessit/commit/0a04fd3948441178c63d661976e2af273aa83676)) * Avoid guessing of to as episode title when Sxx.to.Sxx ([`0045b7d`](https://github.com/guessit-io/guessit/commit/0045b7df16471f324da5913071d4b1fc7db78a3e)) * Enhance season/episode guessing ([`6c0f8d4`](https://github.com/guessit-io/guessit/commit/6c0f8d46005c9079a8c78a9c1af891c2a72f34e0)) * Add ~ to episode/season range separators ([`c7e0ff6`](https://github.com/guessit-io/guessit/commit/c7e0ff6137bd61bbc3fbb8646d3eb32c9faf3e61)) * Enhance complete guessing Close #310 ([`d954db0`](https://github.com/guessit-io/guessit/commit/d954db0d150ce44d5cccb451720aeeac10d79a96)) * Fix tests on python 3 ([`09b9c17`](https://github.com/guessit-io/guessit/commit/09b9c170dcfe1c5825b36d43a71e653dd1c44126)) * Encode options like input_string Close #326 ([`e3dd732`](https://github.com/guessit-io/guessit/commit/e3dd732953ad7c55d3fe0547a80a7d8a4c6751fd)) * Enhance season/episode range guessing Close #339 Close #287 ([`7e71e66`](https://github.com/guessit-io/guessit/commit/7e71e66040eda1baac25b61b02adae73a8be1be7)) * Fix cd_count issue with x264-CD... Close #316 ([`e8b91af`](https://github.com/guessit-io/guessit/commit/e8b91afce81cf4797c9db7900b80db4b1aa1e4bc)) * Fix part property ([`215bad6`](https://github.com/guessit-io/guessit/commit/215bad6f16fc77b70627462c90f4db1266368f5d)) * Better release_group guessing. Close #313 ([`7636ed3`](https://github.com/guessit-io/guessit/commit/7636ed3bd0f6cfa21b4676aab87f0a6d4841c4a1)) * Fix pylint issue. ([`8423caf`](https://github.com/guessit-io/guessit/commit/8423cafc388b8967497d4d9ba4ec759ef254fea8)) * Drop unicode conversion for -T/-G options Close #326 ([`ea95691`](https://github.com/guessit-io/guessit/commit/ea956918099a877c2d5f014d1519c64ab18dfe73)) * Add support for SxxEPxx pattern Close #338 ([`0b73fec`](https://github.com/guessit-io/guessit/commit/0b73fec02a0b47e796d2a692087d15da212326fa)) * Add #328 unit test ([`2750b7b`](https://github.com/guessit-io/guessit/commit/2750b7bcf7b01a144c1ca376692c6c6b6216c9ba)) * Better audio_channels guess when suffixed with ch Close #328 ([`acc5061`](https://github.com/guessit-io/guessit/commit/acc50612c7339bde3fc87ada4e4ecc7b3e50257c)) * Enhance season list support Close #336 ([`852cbaa`](https://github.com/guessit-io/guessit/commit/852cbaa87871a5445311e999b9aa7019615be028)) * Upgrade rebulk to 0.7.4 Close #306 ([`eae627b`](https://github.com/guessit-io/guessit/commit/eae627b1bdd8a5d1c485be73b38052ada312bf51)) * Fix invalid guess with --type episode Close #335 ([`733b746`](https://github.com/guessit-io/guessit/commit/733b7462b3b1d67752a9ada4137f31421c15b263)) * Add workaround for zest.releaser twine issue See https://github.com/zestsoftware/zest.releaser/issues/183 ([`cc7b59c`](https://github.com/guessit-io/guessit/commit/cc7b59ca850b09caa4b6d2ce951424b5cac596f6)) * Back to development: 2.1.1 ([`ed8a57c`](https://github.com/guessit-io/guessit/commit/ed8a57c0a0707646e17a3c8451c3d72a0eba9b17)) * Preparing release 2.1.0 ([`e1dc01e`](https://github.com/guessit-io/guessit/commit/e1dc01ed72ec54e94a5500d9acaa79818ece5d10)) * Update changelog ([`93d4367`](https://github.com/guessit-io/guessit/commit/93d4367af608dfc3e68fe8f618d9ff0d62d369a0)) * Add Rebulk version when running guessit --version Close #322 ([`99cb5b3`](https://github.com/guessit-io/guessit/commit/99cb5b3687b68901633ec050142ec9bc1f568406)) * Remove python-dateutils dependency version constraint Close #329 ([`484f308`](https://github.com/guessit-io/guessit/commit/484f308a8e2d7e6f23740d5052b7f6f26cc1d4f7)) * Fix pylint issue with python 2 ([`66fe4ce`](https://github.com/guessit-io/guessit/commit/66fe4cee6039d97c5433a82bc8e3e49f1f147768)) * Drop regex native module support ([`11658fa`](https://github.com/guessit-io/guessit/commit/11658fac86e3d4132bc535522c3982f99ceb6769)) * Fix pylint issues ([`9943db5`](https://github.com/guessit-io/guessit/commit/9943db5e746c394b77e4f67515f675fc3112d845)) * Merge pull request #325 from ratoaq2/format_video_codec_without_seps Enhances format and video_codec detection when no separators between … ([`bb6e778`](https://github.com/guessit-io/guessit/commit/bb6e7780b14f5c46b04b0573cbe2451c3cf764c3)) * Merge pull request #324 from ratoaq2/enhance_screen_size Enhance screen_size to detect 720pHD and 1080pHD ([`2adce40`](https://github.com/guessit-io/guessit/commit/2adce409afa2c8552a5f87bc7475b93a314d630c)) * Upgrade rebulk to 0.7.3 ([`5aabd3e`](https://github.com/guessit-io/guessit/commit/5aabd3ee76396d0fef7904d375e1817ad3b8e77c)) * Enhances format and video_codec detection when no separators between themselves ([`b5c6632`](https://github.com/guessit-io/guessit/commit/b5c66326157d9bba541b81ed8c9cc2d27d421faa)) * Enhance screen_size to detect 720pHD and 1080pHD ([`dc52959`](https://github.com/guessit-io/guessit/commit/dc52959fbca1a9e4a774580f513e8575ac189e10)) * Avoid season/episode to guessed when pattern is included inside other word Close #242 ([`67b8ac7`](https://github.com/guessit-io/guessit/commit/67b8ac712cb28af6b2e31a8b11a47735b65d59c4)) * Avoid title to be guessed as website. Close #298 ([`57966ab`](https://github.com/guessit-io/guessit/commit/57966abac1b4ab9f9ba949d383c6837ed168ec4f)) * Guess Dolby keyword as DolbyDigital audio_codec Close #295 ([`b8be99b`](https://github.com/guessit-io/guessit/commit/b8be99b88e41c1349c341eb8406b7cc6ddb400fa)) * Fix issue when running guessit in non-interactive shell with python 2 Close #293 ([`7615788`](https://github.com/guessit-io/guessit/commit/7615788505cfa8d73c5ae09091f4da71f062c835)) * Fix invalid conflict solving occuring between SssEee and ssXee patterns. Close #286 ([`eb8229f`](https://github.com/guessit-io/guessit/commit/eb8229fd37c55a65e1f5502873a50b460149c27f)) * Add docs to pytest ignore ([`f130254`](https://github.com/guessit-io/guessit/commit/f130254df26d64379fd58bf4a89fd387d6f5f884)) * Merge pull request #320 from ratoaq2/subtitle_language_fix Fix subtitle language detection when using subtitle_suffixes ([`977bf25`](https://github.com/guessit-io/guessit/commit/977bf25ccd0ce057c3c2f52a204ef641a1ae2244)) * Fix subtitle language detection when using subtitle_suffixes ([`45e5e85`](https://github.com/guessit-io/guessit/commit/45e5e855d208d2232bab49398d8ae08b24b5bfa7)) * Merge pull request #314 from ratoaq2/release_group_fix_313 Fix for #313 ([`de2bf05`](https://github.com/guessit-io/guessit/commit/de2bf05dc80706d1d17d22af5aae78a2b569274f)) * Adding container, other and language as previous scene_names for SceneReleaseGroup rule ([`2e20069`](https://github.com/guessit-io/guessit/commit/2e2006983dae84d3e1dbd474415673352a507534)) * Update changelog ([`61303fa`](https://github.com/guessit-io/guessit/commit/61303fa009ff3b17cd91be7a18fcccec8642d027)) * Keep langage if it exactly match the hole Close #269 ([`5bd2ccb`](https://github.com/guessit-io/guessit/commit/5bd2ccbf604eb2971b341e3fadf59afd7c593bb6)) * Remove properties ignore next to title when they are uppercase ([`77c2c7e`](https://github.com/guessit-io/guessit/commit/77c2c7e9733cd89fd16669627a641880ef9ad2a0)) * Add subbed, custom sub and custom subbed as subtitle markers ([`baf7f93`](https://github.com/guessit-io/guessit/commit/baf7f93ff1366147ccb8aa4b8689c0ac133384a0)) * Back to development: 2.1.0 ([`1163c2c`](https://github.com/guessit-io/guessit/commit/1163c2c59d5c836fe5fa31f7eef6b000ae25bec3)) * Preparing release 2.0.5 ([`4e9ee0a`](https://github.com/guessit-io/guessit/commit/4e9ee0ab3ed9a2227841f9f94a92cb7fbb5f5f48)) * Update changelog ([`3276ff6`](https://github.com/guessit-io/guessit/commit/3276ff6de6356972112b0cfd507af484ed7cac8c)) * Make audio_codec AC3D a synonym of AC3 ([`55ba043`](https://github.com/guessit-io/guessit/commit/55ba043406026abc0dbf3ef68d3a81296de61110)) * Fix pylint issues ([`3cf2391`](https://github.com/guessit-io/guessit/commit/3cf239159a956a3ec7786825c089ab180d47f6d8)) * Add support for titles containing dots Close #278 ([`91f7441`](https://github.com/guessit-io/guessit/commit/91f7441e6cb18d4f0e17ef434fc392a7ea7f0700)) * Lock python-dateutil version See https://github.com/dateutil/dateutil/commit/2d42e046d55b9fbbc0a2f41ce83fb8ec5de2d28b#commitcomment-17032106 Close #284 ([`7d3a22a`](https://github.com/guessit-io/guessit/commit/7d3a22a75075b730785e024a037242af96dc9fd6)) * Fix docstring ([`2b54491`](https://github.com/guessit-io/guessit/commit/2b544919af30fcbfec5e6dcc03694f7fcfbb2e36)) * Fix docs invalid properties and guessit -p listing Close #283 ([`a18f827`](https://github.com/guessit-io/guessit/commit/a18f827b45cb6495dd3f340e1d2030b7ebe67830)) * Update docs with guessit-io organization ([`28b6789`](https://github.com/guessit-io/guessit/commit/28b67896fc26a0473d5738526a45d4b4003e6c80)) * Remove unicode stuff from docs ([`7d7f9c0`](https://github.com/guessit-io/guessit/commit/7d7f9c01554a22c33a94f3fbdf590e04b6fc7494)) * Upgrade rebulk to 0.7.1 ([`8192668`](https://github.com/guessit-io/guessit/commit/8192668d90319e22331905aa59c737a3c6fc542f)) * Back to development: 2.1.0 ([`c298cc0`](https://github.com/guessit-io/guessit/commit/c298cc014573e881f6c8db46031858d98ebcfb10)) * Preparing release 2.0.4 ([`d9ea60d`](https://github.com/guessit-io/guessit/commit/d9ea60d641ebfd8edaa4a9c4293305dbb9e1cebf)) * Add GuessitException Report to get information when guessit crash on a guess. ([`58e27fc`](https://github.com/guessit-io/guessit/commit/58e27fcdf92b226561edaa647d390381e125bf80)) * Fix issues related to backslashes Close #265 Close #266 ([`3ee4ac0`](https://github.com/guessit-io/guessit/commit/3ee4ac0bd2da5b67c9564610756066d1ad1b34b8)) * Remove possible path separators from patterns Close #264 ([`27bc2b3`](https://github.com/guessit-io/guessit/commit/27bc2b391a84e69b2afd3faa9beb9bd93d20001e)) * Back to development: 2.1.0 ([`3f7284a`](https://github.com/guessit-io/guessit/commit/3f7284a635608ee373259227c79d95f45d32c46a)) * Preparing release 2.0.3 ([`0f72c3d`](https://github.com/guessit-io/guessit/commit/0f72c3d4854a9d91b61833a776bdb31e575644c8)) * Update history ([`d3aa378`](https://github.com/guessit-io/guessit/commit/d3aa37834063d5d3244194851b3c0c6f1b077fcd)) * Back to development: 2.1.0 ([`b2b43ab`](https://github.com/guessit-io/guessit/commit/b2b43ab0376a663e32d765c96cdd2b35c04a6896)) * Preparing release 2.0.2 ([`e3d665f`](https://github.com/guessit-io/guessit/commit/e3d665f8268abe204b3db7352ce08b8faf9471d5)) * Update history ([`5109e63`](https://github.com/guessit-io/guessit/commit/5109e634076fb34651d6eac2d487583e3f2ac419)) * It now works with pypy ([`4fe0926`](https://github.com/guessit-io/guessit/commit/4fe09269f79902f9371a59ae8d9d762e69514b77)) * Use utf-8 to decode/encode input unicode string ([`c9e5efd`](https://github.com/guessit-io/guessit/commit/c9e5efd6c10128ceede989ceac3cfc0bdd0fe20c)) * Back to development: 2.1.0 ([`be0586e`](https://github.com/guessit-io/guessit/commit/be0586e0832969d408e59b06d093a7bcbc6a8e68)) * Preparing release 2.0.1 ([`d2edcac`](https://github.com/guessit-io/guessit/commit/d2edcac4b9fa3daa4df1bed0f25a125282395812)) * Add support for all type of string with python 2 and python 3 ([`e24a4b0`](https://github.com/guessit-io/guessit/commit/e24a4b05c1de6d25f7b9b6f0cb328ed3e035c8d6)) * Back to development: 2.1.0 ([`04839ac`](https://github.com/guessit-io/guessit/commit/04839ac9ec26a633618511b7b99b84c5f628ba57)) * Preparing release 2.0.0 ([`2dd6ce2`](https://github.com/guessit-io/guessit/commit/2dd6ce262cdcd5725a7e01a786c92b2351b19b28)) * Fix invalid RST description on Pypi ([`50ddb30`](https://github.com/guessit-io/guessit/commit/50ddb300a9caabc5b42b15f83dc945ed6fc98b25)) * Add more builds ([`122b1f8`](https://github.com/guessit-io/guessit/commit/122b1f8f610157bc2fe38f4db0087eed57042353)) * Back to development: 2.0rc9 ([`36af0b5`](https://github.com/guessit-io/guessit/commit/36af0b592f949c63fa9d8ef2e244583b89b545b9)) * Preparing release 2.0rc8 ([`6ea0851`](https://github.com/guessit-io/guessit/commit/6ea08514eedbd42a27c5d376a2f81ae4c2cc299d)) * Make regex module optional using rebulk chain feature Close #257 ([`fc3ada8`](https://github.com/guessit-io/guessit/commit/fc3ada8cf11695430d1f59ca5f51d4f53a75cb66)) * Better setup.py and requirements ([`a0e95db`](https://github.com/guessit-io/guessit/commit/a0e95db74e240f37bdef0d85369605bca71cf132)) * Add HuBoard badge ([`963bdc3`](https://github.com/guessit-io/guessit/commit/963bdc34e2716967fb7558bc51ad99e27eb62251)) * Back to development: 2.0rc8 ([`9e9f36e`](https://github.com/guessit-io/guessit/commit/9e9f36ebb702b1dd36d757a44d3a48f660a470fb)) * Preparing release 2.0rc7 ([`bd1f50c`](https://github.com/guessit-io/guessit/commit/bd1f50c23d547ad8a3e3b5eb11edafc341846f1e)) * Fix packaging issue on Python 2 ([`2b7c4d7`](https://github.com/guessit-io/guessit/commit/2b7c4d7ae6bc31d1602f62ff898936dc287beace)) * Back to development: 2.0rc7 ([`74ee5c1`](https://github.com/guessit-io/guessit/commit/74ee5c12fc2fff5885f0eb8e1dc83cb0eb02ebd1)) * Preparing release 2.0rc6 ([`058e7d2`](https://github.com/guessit-io/guessit/commit/058e7d2838a47d062fe0c92de9df4a6f4e863e4f)) * Update history ([`a4b5705`](https://github.com/guessit-io/guessit/commit/a4b5705902af76bbec88c01ecc559c42502a420c)) * Oops, invalid python version check ([`e3693a7`](https://github.com/guessit-io/guessit/commit/e3693a7abc7cadb25f6abd87e86cd45f76f13407)) * Add more short words to exclusion list for language guessing Close #256 ([`d0bd6c0`](https://github.com/guessit-io/guessit/commit/d0bd6c0b51cde554f9c610b0e0e80eb0cde98ea9)) * Handle UTF-8 encoding in setup.py (README/HISTORY) ([`1782213`](https://github.com/guessit-io/guessit/commit/178221363ea0a1607390433c3c0bb9b4df41fcb1)) * Back to development: 2.0rc6 ([`2e246c8`](https://github.com/guessit-io/guessit/commit/2e246c8f1b1bbe4a9aa0326662d524af743e012f)) * Preparing release 2.0rc5 ([`23d74f6`](https://github.com/guessit-io/guessit/commit/23d74f60c851e666c1ea461cdda078bc6280f6ee)) * Update history ([`39efe20`](https://github.com/guessit-io/guessit/commit/39efe202696e245960d1bae4cd79e7d0e92c7982)) * Add Jordan to excluded words for language guessing ([`0c805cd`](https://github.com/guessit-io/guessit/commit/0c805cdf7c7a8137d0d9e81872bc590210df97c1)) * Add more possible values in other property (and AVCHD as h264) Close #243 ([`898cb3d`](https://github.com/guessit-io/guessit/commit/898cb3d93ca226d4a3bc09463f2adf56a98f4782)) * Guess film number only if less than 100 Close #245 ([`a693b69`](https://github.com/guessit-io/guessit/commit/a693b69f7b60e05fecec44b8e92b5efc7666ed51)) * Add priority to Screener validation to avoid Scrubs collision Close #246 ([`57c18a5`](https://github.com/guessit-io/guessit/commit/57c18a59c20ed2bdc479480ce068a9a90434c021)) * Add XXX tag value to other property Close #246 ([`b4b3341`](https://github.com/guessit-io/guessit/commit/b4b334186cf8f7946d3eae3ceac4413aece8a153)) * Add more words to common words for language guessing Close #250 ([`a76b23e`](https://github.com/guessit-io/guessit/commit/a76b23ee1ab42a3d6488aebb48bf6cdb3f2a160b)) * Add mimetype property to the docs Close #252 ([`fd522b0`](https://github.com/guessit-io/guessit/commit/fd522b0944c4d0ed9fe7cb82c7fe00a5ae305955)) * Rename alternativeTitle to alternative_title and add it to docs Close #253 ([`8114ae6`](https://github.com/guessit-io/guessit/commit/8114ae616dad6a5090a6dd226c532a96e4c0dd95)) * Capitalize container property Close #255 ([`95dd3c4`](https://github.com/guessit-io/guessit/commit/95dd3c4069d940524c741512bcb8a57c78629eb2)) * Add Dolby Atmos audio_codec support Close #249 ([`a39e019`](https://github.com/guessit-io/guessit/commit/a39e0191213db9fa1eb4a40d88fcba0f9a0dc69d)) * Remove country in title rule ([`e6de767`](https://github.com/guessit-io/guessit/commit/e6de767777e195900a3d95e12768ab2b37dc8e41)) * Remove old repository references ([`43830f6`](https://github.com/guessit-io/guessit/commit/43830f6d4c45eeb616bcc0eb0fd8448cc7d5fa53)) * Add docker section to README ([`0aaf76b`](https://github.com/guessit-io/guessit/commit/0aaf76b6dfcb38a12785f585924158c651aa57d1)) * Back to development: 2.0rc5 ([`bbf35e0`](https://github.com/guessit-io/guessit/commit/bbf35e0be3926ebaa13f3977286cb0f65914a8bc)) * Preparing release 2.0rc4 ([`2cf39ce`](https://github.com/guessit-io/guessit/commit/2cf39ce7c40eacfd1b98d3e6654cbf12e2b66f16)) * Enhance MANIFEST.in and add some ignore to check-manifest ([`02d26fd`](https://github.com/guessit-io/guessit/commit/02d26fddd10724d21dc89dd32bc3f716431f526e)) * Rename `audio_codec` value `true-HD` to `trueHD` ([`6a96441`](https://github.com/guessit-io/guessit/commit/6a964414e05d825d8933cb655b1bf9b738f9ee77)) * Add exotic `screen_size` patterns support like `720hd` and `720p50`. ([`34d0d6a`](https://github.com/guessit-io/guessit/commit/34d0d6aa9dc436b4fe1c040066bcbef4d4bcc522)) * Dockerize ([`0981eba`](https://github.com/guessit-io/guessit/commit/0981eba4fa7080ca05637ffa15a0aff4083d74ec)) * Fix pylint issues ([`5c9003b`](https://github.com/guessit-io/guessit/commit/5c9003be533384304812f71d0f26aab5468318ff)) * Fix travis CI pylint call ([`f998105`](https://github.com/guessit-io/guessit/commit/f998105bc4116fbdd32406611080a561debd7047)) * Upgrade to pylint 1.5.0 ([`88c6376`](https://github.com/guessit-io/guessit/commit/88c6376e95ee6d2c57b14f73748f77c2a4c086b9)) * Add docs and prepare switch to master branch ([`d560755`](https://github.com/guessit-io/guessit/commit/d560755d6b6f55d292f0ebfbc0983479a526c7f9)) * Back to development: 2.0rc4 ([`c74aa1c`](https://github.com/guessit-io/guessit/commit/c74aa1c2fa87abc9f07e3ae8d4aa19dc33cbdbcd)) * Preparing release 2.0rc3 ([`d5dbf27`](https://github.com/guessit-io/guessit/commit/d5dbf27b70d4aaa58de01d21a2c4df798dbd7289)) * Add version to main module ([`66413d5`](https://github.com/guessit-io/guessit/commit/66413d54c2f328e11be9b87fdb913d81e60ca6dc)) * Back to development: 2.0rc3 ([`9dc375b`](https://github.com/guessit-io/guessit/commit/9dc375bc71107c087cf4f7e03562968d1f3a9f5a)) * Preparing release 2.0rc2 ([`19eb96a`](https://github.com/guessit-io/guessit/commit/19eb96a0b631ad8b934df74a8e9e22df08acb311)) * Update history ([`091f399`](https://github.com/guessit-io/guessit/commit/091f399f654cc9ed230a8790363303f4558fd742)) * Fix possible NoneType error ([`ea139f0`](https://github.com/guessit-io/guessit/commit/ea139f0ef1edf941321cb6abc114d3350b4cc1ba)) * Remove ; from titles separators and & from separators ([`59a99a2`](https://github.com/guessit-io/guessit/commit/59a99a283d61fa9bce315665cca8e301d82e3302)) * Single digit episodes are now guessed for --type episode instead of --episode-prefer-number ([`bfd21d3`](https://github.com/guessit-io/guessit/commit/bfd21d3f10f81dc437fdee68239a53a5f380c043)) * Back to development: 2.0rc2 ([`f1df1e3`](https://github.com/guessit-io/guessit/commit/f1df1e335f11509b65c94037c5f0ebc7e15f4fe7)) * Preparing release 2.0rc1 ([`05d64c1`](https://github.com/guessit-io/guessit/commit/05d64c126a2110b784fd81fa7d5760962d642c65)) * Update README for Release Candidate ([`e473dc3`](https://github.com/guessit-io/guessit/commit/e473dc36c5a82e33ce8ba6d3e569ecbf74243555)) * Fallback to default title guessing when expected title doesn't match ([`301a9bd`](https://github.com/guessit-io/guessit/commit/301a9bd58f6b67047dfe68cc082c026fcd173359)) * Back to development: 2.0b5 ([`3151faa`](https://github.com/guessit-io/guessit/commit/3151faa26864f954373dfd8022d4503f58a2e5cd)) * Preparing release 2.0b4 ([`5e99e5e`](https://github.com/guessit-io/guessit/commit/5e99e5ec7da21fb909e2bafece7aadb4dc7c5fac)) * Update history ([`479ee4d`](https://github.com/guessit-io/guessit/commit/479ee4d6c34f8cce1f7b22bc836105acb4061e81)) * Fix pylint issues ([`d24a3a9`](https://github.com/guessit-io/guessit/commit/d24a3a9622cc93b6799bd8421ee20ea459d3a0a9)) * Add better support for -T option ([`27898a9`](https://github.com/guessit-io/guessit/commit/27898a9641154d33ee59ba3bb51fdc341e43c5fb)) * Add support for unicode in -T/-G options ([`2f74c35`](https://github.com/guessit-io/guessit/commit/2f74c35002153b65fbd8b107454747a1f9dd18e1)) * Fix expected title Close #238 ([`95b1f71`](https://github.com/guessit-io/guessit/commit/95b1f7101a0ddcb6f6b8b5d3258d91add650d519)) * Give priority to explicit title ([`2d8b53f`](https://github.com/guessit-io/guessit/commit/2d8b53fc8f3d84b5a66d2bad49a080524abcaadf)) * Implement --verbose option ([`94fb398`](https://github.com/guessit-io/guessit/commit/94fb398928499840f5e6ad9d526685148cc58a90)) * Add validation rule for single digit episode to avoid false positives. ([`4570eaa`](https://github.com/guessit-io/guessit/commit/4570eaa0cfc4eafd3f8b5276b3e750a0e3a8b8e3)) * Add --expected-group option ([`9c7f7b7`](https://github.com/guessit-io/guessit/commit/9c7f7b735a50c28f7196a871cd5f0612172cb70d)) * Back to development: 2.0b4 ([`c1beb0e`](https://github.com/guessit-io/guessit/commit/c1beb0e77a9ae20b36bc59fc6fa8eb77a06c3c10)) * Preparing release 2.0b3 ([`e84b645`](https://github.com/guessit-io/guessit/commit/e84b6458a41d29c6211726462ab5d133a719f927)) * Fix IndexError when input has a closing group character with no opening one before. ([`c303db6`](https://github.com/guessit-io/guessit/commit/c303db6a4b9709bc7ce4056f2714ae9ecdc96e8b)) * Ensure date is found when conflicting with episode/season ([`2fb1c64`](https://github.com/guessit-io/guessit/commit/2fb1c64083b6da3a21d91ae7a8823f6334463ac1)) * Add better space support in episode/season properties ([`b6ce12b`](https://github.com/guessit-io/guessit/commit/b6ce12bc1b1c84a7a2f83f94dfc3f01afeb63390)) * Update usage in README.rst ([`e4e2827`](https://github.com/guessit-io/guessit/commit/e4e28277f2681f77e6b2a2fc375950e9519fdea5)) * Avoid uuid and crc32 collision with episode/season properties ([`a56a7f3`](https://github.com/guessit-io/guessit/commit/a56a7f3453991effaab319bcc034dc45c20179e2)) * Update history ([`3ab8726`](https://github.com/guessit-io/guessit/commit/3ab87263bdf35b50631fcbaaaccde85ead16cbf7)) * Update Development Status classifier ([`9a05b28`](https://github.com/guessit-io/guessit/commit/9a05b288fb0d13876bc7202467f743992ddeecb7)) * Force implicit option in CLI ([`2ad4c4d`](https://github.com/guessit-io/guessit/commit/2ad4c4d882027b13e1badfbcf28b866e20ed277a)) * Add --type option ([`7ba5ae3`](https://github.com/guessit-io/guessit/commit/7ba5ae302a84c9fdac53d517762c13ed2c4aecd7)) * Add rebulk implicit option support ([`ed08358`](https://github.com/guessit-io/guessit/commit/ed083582673e4d15272850b812e0dad840a78cd3)) * Add support for Part with no space before number ([`043a032`](https://github.com/guessit-io/guessit/commit/043a032c7801f66e614f2852c929955a9a6242a5)) * Back to development: 2.0b3 ([`ca65d05`](https://github.com/guessit-io/guessit/commit/ca65d05472e7fcc724305938b2fa762769ab1b6d)) * Preparing release 2.0b2 ([`dd8a7bf`](https://github.com/guessit-io/guessit/commit/dd8a7bffe0ba2a6f5481169bd303da7e627328c8)) * Remove pypy environment from tox configuration ([`07725f3`](https://github.com/guessit-io/guessit/commit/07725f3e77ffd541bf8002737ef9e43210bc334f)) * Add python 2.6 support ([`4dcc48a`](https://github.com/guessit-io/guessit/commit/4dcc48a6687a88434f79082ecd52810537a41164)) * Remove rebulk master from dev requirements ([`3b4c467`](https://github.com/guessit-io/guessit/commit/3b4c4679227871c04969913a7407adb7c21e2173)) * Back to development: 2.0b2 ([`6284423`](https://github.com/guessit-io/guessit/commit/6284423e792edc9725b7cef197627a8ac314261e)) * Preparing release 2.0b1 ([`356006a`](https://github.com/guessit-io/guessit/commit/356006a422dffbe07487d8cd25d69f88a5b179fa)) * Update history ([`f8f25de`](https://github.com/guessit-io/guessit/commit/f8f25de032aecdf63fd9c7433d3be42b7c3b7d49)) * Bump rebulk 0.6.1 and enhance title guessing ([`ca469a9`](https://github.com/guessit-io/guessit/commit/ca469a9eaed2f02a7b41c2a8e36d86fbae151015)) * Update migration ([`6733b78`](https://github.com/guessit-io/guessit/commit/6733b785dd0126a34987c6e2018cd1c713e29e30)) * Avoid crash when displaying properties and yaml module is not available. ([`af68081`](https://github.com/guessit-io/guessit/commit/af680817c5cd7614e3b41ac1f85dfe916e848292)) * Increase code coverage ([`f657685`](https://github.com/guessit-io/guessit/commit/f6576854e191ea12c3f2959dbab9a317c8d15324)) * Rename properCount to proper_count and add it to introspector ([`0fe54b6`](https://github.com/guessit-io/guessit/commit/0fe54b694a073d74b71a18d376757bede05bfdb7)) * We are in Beta now ([`f78b201`](https://github.com/guessit-io/guessit/commit/f78b201867ae92e5ad94b01b137c04341a66727a)) * Back to development: 2.0b1 ([`324adb8`](https://github.com/guessit-io/guessit/commit/324adb84fabf2639a23f7719936400ca3e6d9499)) * Preparing release 2.0a4 ([`11d4e51`](https://github.com/guessit-io/guessit/commit/11d4e5153511494032a0354486f381d585cc4fe6)) * Update history ([`07b7f90`](https://github.com/guessit-io/guessit/commit/07b7f907aa1ffe3479ad469da181733a0d8b7cfb)) * Bump rebulk 0.6.0 and add -p/-V options ([`929e499`](https://github.com/guessit-io/guessit/commit/929e499b181d976c41719e9f1bc7ea8c8afd55c6)) * Update HISTORY format to match zest.releaser ([`62b0b08`](https://github.com/guessit-io/guessit/commit/62b0b08fc1d4e6ca357f1b50c42902a226e7bbb1)) * Fix pylint issue on python 2.7 ([`12dba88`](https://github.com/guessit-io/guessit/commit/12dba88ad2eed11fe0ce095c0ed89f7502fc1a04)) * Back to development: 2.0a4 ([`87eeb21`](https://github.com/guessit-io/guessit/commit/87eeb212928bfc5df2d863512c0a34205020a05b)) * Preparing release 2.0a3 ([`a701818`](https://github.com/guessit-io/guessit/commit/a701818f1cdbda4396c333c663e84a7ebe6b9973)) * Refactor api and rules to use builder pattern and allow customization ([`fb36ecc`](https://github.com/guessit-io/guessit/commit/fb36ecca8aae75576d2509ce284599a0195c09d0)) * Back to development ([`d3c01e5`](https://github.com/guessit-io/guessit/commit/d3c01e5552531aefc29ef64f4f4ad581f6e39fed)) * Add zest.releaser[recommended] support ([`adc5b78`](https://github.com/guessit-io/guessit/commit/adc5b78d102cef901ff38814e83a968f82a1dcc1)) * Release 2.0a2 ([`cc6e34b`](https://github.com/guessit-io/guessit/commit/cc6e34be52394ec31cc86d99e23662de652cdcde)) * Raise TypeError instead of AssertionError when non text is given to guessit API. Thanks @diaoul ([`dfe0d10`](https://github.com/guessit-io/guessit/commit/dfe0d106157e4f5934cb7baa12b26cfb43426780)) * Release 2.0a1 ([`7010401`](https://github.com/guessit-io/guessit/commit/70104011b8a8702980f9884b43b1adbbce4de857)) * Rename properties and update MIGRATION docs ([`eaee32e`](https://github.com/guessit-io/guessit/commit/eaee32e65987a8a381015748e3dc64afeb500ba8)) * Fix pylint issue in benchmark test ([`224c398`](https://github.com/guessit-io/guessit/commit/224c39873dcbf9c129c4fa39fc2e50a02af6c62a)) * Use latest pip on TravisCI and use released version of rebulk ([`245fadf`](https://github.com/guessit-io/guessit/commit/245fadf6f18c9987f231ef6120fde7112f66c724)) * Add benchmark test ([`c62b45c`](https://github.com/guessit-io/guessit/commit/c62b45c86f6d3e36980cf16f4bb009de10a48f8a)) * Fix doctests for python 2.7 ([`125bfc1`](https://github.com/guessit-io/guessit/commit/125bfc1f7f9f87d2df10a6b6f779c80e3df1acdd)) * Fix issues related to str/unicode in python 2.7 ([`e02060c`](https://github.com/guessit-io/guessit/commit/e02060c04fc03f7226c110cafe28e8963a33ec23)) * Add docs and better migration path ([`1ae6ca0`](https://github.com/guessit-io/guessit/commit/1ae6ca07f5ab33df3f25a092a908556075d796d1)) * Remame extension property to container with extension tag ([`ee57672`](https://github.com/guessit-io/guessit/commit/ee576724079112f1765464786c5c8e6f14be0ffd)) * Add mimetype property ([`aba054a`](https://github.com/guessit-io/guessit/commit/aba054a6ec684a4ae437d59c7ddff43f30534f2e)) * Rename type series value to episode ([`b5cf22f`](https://github.com/guessit-io/guessit/commit/b5cf22f297a9ec6a8b3c1122dee0b90289c9e2d7)) * bump rebulk version ([`b2c6917`](https://github.com/guessit-io/guessit/commit/b2c6917be1edbe0a9588bb87672d943350cd1744)) * Add type property, more test cases and fixes ([`53c5522`](https://github.com/guessit-io/guessit/commit/53c5522b23046dab05364bac3078f96660fe7d3b)) * Add more episode tests along with small fixes ([`08e7c5e`](https://github.com/guessit-io/guessit/commit/08e7c5ede5f9d970f52c9aab90823459712a6b21)) * Add container property ([`9100d52`](https://github.com/guessit-io/guessit/commit/9100d52c19f86d90db0ee94942359d5babf57101)) * Make python setup.py test works on an new environment ([`dfbfc0c`](https://github.com/guessit-io/guessit/commit/dfbfc0c10677bff9a1b4e48d3ace4dba8da7c9b7)) * Guess version property when version is detached from episodeNumber ([`938b851`](https://github.com/guessit-io/guessit/commit/938b8512a073a494c1b119471b3337198da62764)) * Add more series tests ([`6e4cc68`](https://github.com/guessit-io/guessit/commit/6e4cc68e38583c0ec5c26be9bee353017f0c30bd)) * Add more audioCodec tests ([`7fb7da7`](https://github.com/guessit-io/guessit/commit/7fb7da7bdde47bc5a0f5b5f768d57f0178619aaa)) * Fix possible NoneType error ([`158eaf3`](https://github.com/guessit-io/guessit/commit/158eaf371ebfc6c998ab2a81f405eaa02e15898a)) * Add + as a separator for episodeNumber list ([`af488c5`](https://github.com/guessit-io/guessit/commit/af488c560e55b8535bbdd87e8a7528d9d5c4a356)) * Fix possible conflict between audioChannel and episodeNumber ([`5157b41`](https://github.com/guessit-io/guessit/commit/5157b41fe2b0c31d2dcdeac5e2431f506333946e)) * Add more series test cases ([`b4d54aa`](https://github.com/guessit-io/guessit/commit/b4d54aa044e80963d949bc2414dc6b9db9d3de0c)) * Bump rebulk version ([`418a552`](https://github.com/guessit-io/guessit/commit/418a552630b86300c19321d896fa1a9321cafe14)) * Add expected-title option and refactor title/episodeTitle guessing ([`eeb5fe1`](https://github.com/guessit-io/guessit/commit/eeb5fe18e053f64b839f29106a4e7568ef00042d)) * Add better conflict solver between screenSize and episodeNumber/season ([`da98979`](https://github.com/guessit-io/guessit/commit/da989793178d1e9b6715dfa3e24ac441e198e923)) * Enhance country, language and title properties ([`69e7d11`](https://github.com/guessit-io/guessit/commit/69e7d111defdfb3bbe42c1555cb3b821afc2d1a0)) * Add enhancements for episodeNumber guessing ([`ad60eb6`](https://github.com/guessit-io/guessit/commit/ad60eb6ec65c95d442b115312bad9229ff2dec52)) * Add better season and episode list and count ([`a480741`](https://github.com/guessit-io/guessit/commit/a4807417f5289046ef2298a1764e4044860ad77b)) * Add episode version property ([`76e6d5a`](https://github.com/guessit-io/guessit/commit/76e6d5a3f5b7176973d04015bf2806fd1c27805e)) * Fix title guessing when title is not the first available hole ([`289b92a`](https://github.com/guessit-io/guessit/commit/289b92a4dc7a9086bb8878bc325a4cd351d34764)) * Add support for weak-duplicates (the.100.109.hdtv-lol.mp4 case) ([`876cb68`](https://github.com/guessit-io/guessit/commit/876cb68e1e2616a865e80b8f357290435c96315a)) * Add season_year post_processor to add year property when season value is a valid year. ([`14e8444`](https://github.com/guessit-io/guessit/commit/14e844450fd5e4d395f36dcdbebc182fadf6df18)) * Add range support for episodeNumber (- character as range) ([`b7a7c2d`](https://github.com/guessit-io/guessit/commit/b7a7c2d06b09d096727dcf478eeccee68a1afc89)) * Add equivalent holes post_processor to include equivalent parts of guessed files in matches list and retrieve best title case. ([`ab700ec`](https://github.com/guessit-io/guessit/commit/ab700ec4179048ee023777962d206d8a4c6fa772)) * Upgrade babelfish to 0.5.5 and add country property support ([`f98c8b7`](https://github.com/guessit-io/guessit/commit/f98c8b7302d76dd991cbbad9f377dfc36d2bc1ca)) * Add better anime releaseGroup support ([`a1d41c8`](https://github.com/guessit-io/guessit/commit/a1d41c864402be4ee32a3758f27730ade8ee8be3)) * Bump rebulk version ([`419c116`](https://github.com/guessit-io/guessit/commit/419c1169100b53aa634e9b8aa7b3581cb457752e)) * Use more toposort for rebulk rules and fix issues with title ([`2a0aca1`](https://github.com/guessit-io/guessit/commit/2a0aca1ec5a166fe1e96c3780847a87d65a4c54f)) * Use latest rebulk Rule API ([`9131068`](https://github.com/guessit-io/guessit/commit/91310682ef058ab2f9d8ade86658df3effdf0025)) * Simplify language prefix/suffix rules ([`496c508`](https://github.com/guessit-io/guessit/commit/496c508cb4d1b3e924ac2779a96bec3a0edd4e4a)) * Avoid using Rebulk Match object in functionnal patterns ([`0a733e4`](https://github.com/guessit-io/guessit/commit/0a733e41345636c9d8b14029922115fca6fb929e)) * Add crc32 property ([`dd232c8`](https://github.com/guessit-io/guessit/commit/dd232c846d14e7eb984333980850d1d18c18e18d)) * Add episodeNumber list support ([`09b7888`](https://github.com/guessit-io/guessit/commit/09b7888055a213931b44c43f2b9eabc2abf26b10)) * Add remove_ambiguous post processor and support better guessing of titles ([`97491e4`](https://github.com/guessit-io/guessit/commit/97491e4d58b67763b3e3757a1e240653f32fab51)) * Add more episodes patterns and properties support ([`140f2fa`](https://github.com/guessit-io/guessit/commit/140f2fa2dc32489bb2d925ba5768b9994d97117f)) * Add alternativeTitle property ([`5c63b96`](https://github.com/guessit-io/guessit/commit/5c63b96a89a4dffb2b4278fbfadbcd3900f7772b)) * Convert language to subtitleLanguage when extension is a subtitle ([`ce169dd`](https://github.com/guessit-io/guessit/commit/ce169dda1e2d081ebfb25e24dc6d51c4ce475564)) * Add private_parent=True to episodes regex to fix various issues ([`dabaa7f`](https://github.com/guessit-io/guessit/commit/dabaa7f088ba3110f64dee93224ed7bc23f92b96)) * Add more season/episodeNumber support ([`485ac71`](https://github.com/guessit-io/guessit/commit/485ac7141005f3f431c0c458f013225d349e2ae7)) * Add weak episodeNumber/season patterns ([`94c6700`](https://github.com/guessit-io/guessit/commit/94c670086d9636a8bc6f5072b22b3fc97bcc782b)) * Add reorder title feature ([`49f7971`](https://github.com/guessit-io/guessit/commit/49f797158cae5deb31caecf85aebd30001017d14)) * Update to latest Rebulk changes ([`01c7751`](https://github.com/guessit-io/guessit/commit/01c775194788cca7e736fb6676a2da954f4af403)) * Add name-only option ([`42aceda`](https://github.com/guessit-io/guessit/commit/42acedaf7c0fdfa192e0aeb61d51a1b788d224e0)) * Keep ",:;-" when cleaning value ([`c318455`](https://github.com/guessit-io/guessit/commit/c31845547bec3cd2ddd223a80e89a470bfe01d63)) * Solve conflict with 'HQ' pattern from audioProfile and other properties ([`df56d69`](https://github.com/guessit-io/guessit/commit/df56d69ef9fbdb68c2fdcf7324b1dfef19745cfa)) * Enhance subtitleLanguage guessing with groups ([`3c7bfa9`](https://github.com/guessit-io/guessit/commit/3c7bfa90cbf0834399378510ada59d78d3c0c741)) * Add scr to excluded word for language guessing ([`4614116`](https://github.com/guessit-io/guessit/commit/46141167993784dc6c33e4f01e0b60e9b295d01c)) * Enhance title/language guessing ([`7ae2bb4`](https://github.com/guessit-io/guessit/commit/7ae2bb4680c952d6559dd018a0eed13ce706e153)) * Update conflict_solver Rebulk API ([`66064aa`](https://github.com/guessit-io/guessit/commit/66064aaf423453bad2fb68574e6b0fc0136e05dd)) * Keep ",;" when cleaning value and ensure bonus is surrounded by separators ([`2938574`](https://github.com/guessit-io/guessit/commit/29385741d0d0bd9777a6370b7a74208664258286)) * Enhance title/language ([`aa518c2`](https://github.com/guessit-io/guessit/commit/aa518c242cc2ef5780ccf9d86d612e8c01fe5816)) * Add idNumber property and enhance audio properties conditions ([`1636ade`](https://github.com/guessit-io/guessit/commit/1636adeabf6b72d4b64c6304635dbaee4153d20e)) * Ignore language if included in title ([`ec8e429`](https://github.com/guessit-io/guessit/commit/ec8e4294fa6eedc0e7e30161bf580e542cb08338)) * Add videoApi property ([`8d50a10`](https://github.com/guessit-io/guessit/commit/8d50a10c87c5b915c6b374e9ef42934e51a606e0)) * Enhance cleanup formatter to remove duplicate spaces ([`62bca67`](https://github.com/guessit-io/guessit/commit/62bca6741cb995b92db9aac88596dbed19c3d4a4)) * Enhance year property when multiple match are found ([`331105c`](https://github.com/guessit-io/guessit/commit/331105c063f104d4ff0e65caf40824bab208dc1f)) * Add more tests for part property ([`ff7e1a4`](https://github.com/guessit-io/guessit/commit/ff7e1a4802514796c1fdff1125c57b91aa642c63)) * Optimize imports ([`f4fb6a8`](https://github.com/guessit-io/guessit/commit/f4fb6a8f3e85c6e9607a9afab4b95ed659b4654d)) * Refactor rules module ([`6bc2e95`](https://github.com/guessit-io/guessit/commit/6bc2e9511733422ff78e24006fc13d6ce3a827ef)) * Fix bonusNumber and filmNumber properties ([`bbd7375`](https://github.com/guessit-io/guessit/commit/bbd7375f42c58cb857f8c5e2738a50a747e94deb)) * Add part property support ([`729ca40`](https://github.com/guessit-io/guessit/commit/729ca401000cc6926e73bf1737953339773b7b3c)) * Add filmSeries and filmNumber properties ([`149e344`](https://github.com/guessit-io/guessit/commit/149e344579c75c001c8213a44c80e2132de86069)) * Fix an issue in bonus property ([`0ac46cf`](https://github.com/guessit-io/guessit/commit/0ac46cfea26e7c40fea5ca59ffdd005b37a69654)) * Add bonusNumber and bonusTitle properties ([`d935606`](https://github.com/guessit-io/guessit/commit/d9356063b368b5999611b77c13c2836248a6baa1)) * Add audioChannels property ([`ae700f2`](https://github.com/guessit-io/guessit/commit/ae700f238e7a7cbbab4b05381e4e4ef3dcf7bdc0)) * Enhance code coverage ([`29ad170`](https://github.com/guessit-io/guessit/commit/29ad170b41316a13f2abceeb5abec80c1eba71bf)) * Fix parse_options for python 2.7 ([`4191b42`](https://github.com/guessit-io/guessit/commit/4191b42325f766d4d1211ae75999e7f453ecf527)) * Fix pylint issues ([`3c0c2b4`](https://github.com/guessit-io/guessit/commit/3c0c2b44bfd11b30979835006e3a0df19d4f3e6c)) * Add more movies tests ([`9e73e69`](https://github.com/guessit-io/guessit/commit/9e73e69e0f667530ebd88fb1a9d88df82d14e06d)) * Fix language tests ([`26aad3a`](https://github.com/guessit-io/guessit/commit/26aad3a1b9f722874127f293a2868bab23d5d35e)) * Fix releaseGroup to remove forbidden words at end of match ([`e2d9afc`](https://github.com/guessit-io/guessit/commit/e2d9afc5fd68eb707ae110b9b5c6e633b408b7f0)) * Add babelfish Contry/Language support in json dumps ([`2637723`](https://github.com/guessit-io/guessit/commit/2637723c36b44edd0dbbf72ddf3865895c3a6144)) * Let dot belong to extension ([`9435bf9`](https://github.com/guessit-io/guessit/commit/9435bf9db5c9f6e3a290a0f3c99b0f1d7cf2cefc)) * Add command line support and relax releaseGroup guessing ([`b5de364`](https://github.com/guessit-io/guessit/commit/b5de3644131ae83bf19feb74a3dd56b94f5c884a)) * Add date property ([`2d68c75`](https://github.com/guessit-io/guessit/commit/2d68c756d43c28388bf5b7658ae7b2fc5f670a5d)) * Add unicode support for python 2.7 ([`ca3cfe7`](https://github.com/guessit-io/guessit/commit/ca3cfe715d12bafac0c34469d503cc9d42f71d55)) * Fix pylint issue ([`88b29f2`](https://github.com/guessit-io/guessit/commit/88b29f227ac1a86691271b4f9edca486d417c534)) * If multiple title are found, prefer one where filepart contains year ([`d4d57bc`](https://github.com/guessit-io/guessit/commit/d4d57bcff769d0a1fc4c2cae76292f06e36c2c7b)) * Fix cdNumberTotal property ([`cf01e1f`](https://github.com/guessit-io/guessit/commit/cf01e1f5283b5bc0aff1579a9ea1d28203239bd9)) * Ignore an anime releaseGroup if scene releaseGroup is found ([`6ef3f7b`](https://github.com/guessit-io/guessit/commit/6ef3f7bb4b0dfd0fd83b4f5c7d03a9aa13f675cc)) * Sort the longuer marker before when matches count is equal ([`9dff7d3`](https://github.com/guessit-io/guessit/commit/9dff7d3ad7aed9ebe97a0a8033964e92fe0116dd)) * Enhance releaseGroup guessing ([`881fd3e`](https://github.com/guessit-io/guessit/commit/881fd3e22329be065ec2186078beecbe8aa89744)) * Fix an issue in format validation rule ([`7deae1a`](https://github.com/guessit-io/guessit/commit/7deae1a7122f83a202a6348ccddd7b89aaaf86aa)) * Add support of entries where most information are not in filename. ([`b12682e`](https://github.com/guessit-io/guessit/commit/b12682e46bc7a1042ae378e41e8408f2a4d378a2)) * Add more movies tests ([`d8f6338`](https://github.com/guessit-io/guessit/commit/d8f63383880a0150df78cc043978d310281ac98e)) * Add cdNumber and cdNumberTotal properties ([`cfe5102`](https://github.com/guessit-io/guessit/commit/cfe5102fc6ef333483745dfa16b639c250a52ee7)) * Add edition property ([`3a62d43`](https://github.com/guessit-io/guessit/commit/3a62d437aeae9b91729ab645bcb3db536b60385d)) * Add other and propertCount properties ([`a25e828`](https://github.com/guessit-io/guessit/commit/a25e8289585b48d612dcf4678699f1dc651dd7fa)) * Add videoProfile and audioProfile properties ([`88c46ba`](https://github.com/guessit-io/guessit/commit/88c46ba80d626e51cc8580d9ecee31f474efb4bc)) * Enhance releaseGroup guessing ([`de3acb1`](https://github.com/guessit-io/guessit/commit/de3acb1f36c508a1103a6fb3c214b88981e249a6)) * Enhance language and subtitleLanguage guessing ([`9db5127`](https://github.com/guessit-io/guessit/commit/9db512700c69c4ecf2362ffa4df39479d330cee0)) * Enhance releaseGroup guessing ([`14374bd`](https://github.com/guessit-io/guessit/commit/14374bde89485443cff08fdf9a5162cbfe7ac747)) * Fix issue in subtitle suffix support ([`8b7cc69`](https://github.com/guessit-io/guessit/commit/8b7cc6961e39a17b00fd8629fdfb5807ad5a42d1)) * Add groups markers (...), [...] and {...} ([`4be239b`](https://github.com/guessit-io/guessit/commit/4be239baf6dc594d5042b9321b1105c6e7e1564c)) * Add language and subtitleLanguage property ([`d369634`](https://github.com/guessit-io/guessit/commit/d36963461657f374e1fa54597bb48523ccf5d957)) * Add episodeTitle property ([`8d4dbcd`](https://github.com/guessit-io/guessit/commit/8d4dbcd89729c4b667f4aa5a3f7969159e4765d8)) * Add releaseGroup property as Rule ([`6bd3864`](https://github.com/guessit-io/guessit/commit/6bd38647bd08f5fb1478a2d6affb3ac828d1b213)) * Use Rule class for title property ([`abeda01`](https://github.com/guessit-io/guessit/commit/abeda01444414a3cb62f0cde9ebe46e75d08799e)) * Fix badges to point to 2.x branch build ([`20e4280`](https://github.com/guessit-io/guessit/commit/20e42801cbb9a68fdae25e0a2022db8fdeb9057d)) * Add title property ([`7a545ad`](https://github.com/guessit-io/guessit/commit/7a545ad008a982260fc3b4848fd65e9e1873f15c)) * Add extension patterns ([`40106df`](https://github.com/guessit-io/guessit/commit/40106dfe26b131eb747649cc17dd1956991aa017)) * Add path markers and give priority on last path element ([`e90225f`](https://github.com/guessit-io/guessit/commit/e90225f54d89b8c11490b9c3e9df77625d9d793d)) * Add format, screenSize, videoCodec, audioCodec, website and year patterns ([`9e640e9`](https://github.com/guessit-io/guessit/commit/9e640e9139eb9affa84bf2ca9c1b9fc3c30853f4)) * Add test for __main__ entry point ([`95a759d`](https://github.com/guessit-io/guessit/commit/95a759d12fdc302b04c104fdd48fe3d28a2c0dc7)) * Add HuBoard link ([`9b988a7`](https://github.com/guessit-io/guessit/commit/9b988a7e8705db19689772fd02cb661120e2d2e4)) * Initial commit ([`dab80bd`](https://github.com/guessit-io/guessit/commit/dab80bd200fd90ab83bc156789ca80c516e7fa0c)) guessit-3.8.0/CONTRIBUTING.md000066400000000000000000000014621453655371700154610ustar00rootroot00000000000000# Contribute GuessIt is under active development, and contributions are more than welcome! 1. Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug. There is a Contributor Friendly tag for issues that should be ideal for people who are not very familiar with the codebase yet. 2. Fork [the repository][] on Github to start making your changes to the **develop** branch (or branch off of it). 3. Write a test which shows that the bug was fixed or that the feature works as expected. 4. Send a pull request and bug the maintainer until it gets merged and published. :) # License GuessIt is licensed under the [LGPLv3 license][]. [the repository]: https://github.com/guessit-io/guessit [LGPLv3 license]: http://www.gnu.org/licenses/lgpl.htmlguessit-3.8.0/Dockerfile000066400000000000000000000002661453655371700152230ustar00rootroot00000000000000FROM python:3.7-alpine MAINTAINER Rémi Alvergnat WORKDIR /root COPY / /root/guessit/ WORKDIR /root/guessit/ RUN pip install -e . ENTRYPOINT ["guessit"] guessit-3.8.0/LICENSE000066400000000000000000000167421453655371700142440ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.guessit-3.8.0/MANIFEST.in000066400000000000000000000003371453655371700147660ustar00rootroot00000000000000recursive-include guessit *.py *.yml *.txt *.ini *.json *.yaml *.yml recursive-exclude guessit *.pyc include LICENSE include *.md include *.yml include *.ini include *.cfg include *.txt include .coveragerc include pylintrc guessit-3.8.0/README.md000066400000000000000000000035451453655371700145130ustar00rootroot00000000000000GuessIt [![Latest Version](https://img.shields.io/pypi/v/guessit.svg)](https://pypi.python.org/pypi/guessit) [![LGPLv3 License](https://img.shields.io/badge/license-LGPLv3-blue.svg)]() [![Build Status](https://img.shields.io/github/workflow/status/guessit-io/guessit/ci)](https://github.com/guessit-io/guessit/actions?query=workflow%3Aci) [![Codecov](https://img.shields.io/codecov/c/github/guessit-io/guessit)](https://codecov.io/gh/guessit-io/guessit) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/relekang/python-semantic-release) GuessIt is a python library that extracts as much information as possible from a video filename. It has a very powerful matcher that allows to guess properties from a video using its filename only. This matcher works with both movies and tv shows episodes. For example, GuessIt can do the following: $ guessit "Treme.1x03.Right.Place,.Wrong.Time.HDTV.XviD-NoTV.avi" For: Treme.1x03.Right.Place,.Wrong.Time.HDTV.XviD-NoTV.avi GuessIt found: { "title": "Treme", "season": 1, "episode": 3, "episode_title": "Right Place, Wrong Time", "source": "HDTV", "video_codec": "Xvid", "release_group": "NoTV", "container": "avi", "mimetype": "video/x-msvideo", "type": "episode" } More information is available at [guessit-io.github.io/guessit](https://guessit-io.github.io/guessit). Support ------- This project is hosted on [GitHub](https://github.com/guessit-io/guessit). Feel free to open an issue if you think you have found a bug or something is missing in guessit. GuessIt relies on [Rebulk](https://github.com/Toilal/rebulk) project for pattern and rules registration. License ------- GuessIt is licensed under the [LGPLv3 license](http://www.gnu.org/licenses/lgpl.html). guessit-3.8.0/dev-requirements.txt000066400000000000000000000000171453655371700172630ustar00rootroot00000000000000-e .[dev,test] guessit-3.8.0/docs/000077500000000000000000000000001453655371700141555ustar00rootroot00000000000000guessit-3.8.0/docs/configuration.md000066400000000000000000000027111453655371700173470ustar00rootroot00000000000000# Configuration files Guessit supports configuration through configuration files. Default configuration file is bundled inside guessit package from [config/options.json][] file. It is possible to disable the default configuration with `--no-default-config` option, but you have then to provide a full configuration file based on the default one. Configuration files are loaded from the following paths: > - `~/.guessit/options.(json|yml|yaml)` > - `~/.config/guessit/options.(json|yml|yaml)` It is also possible to disable those user configuration files with `no-user-config` option. Additional configuration files can be included using the `-c`/`--config` option. As many configuration files can be involved, they are deeply merged to keep all values inside the effective configuration. # Advanced configuration Configuration files contains all options available through the command line, but also an additional one named `advanced_config`. This advanced configuration contains all internal parameters and they are exposed to help you tweaking guessit to better fit your needs. If no `advanced_config` is declared through all effective configuration files, the default one will be used even when `--no-default-config` is used. We're willing to keep it backwards compatible, but in order to enhance Guessit, these parameters might change without prior notice. [config/options.json]: https://github.com/guessit-io/guessit/blob/master/guessit/config/options.json/guessit-3.8.0/docs/index.md000066400000000000000000000163741453655371700156210ustar00rootroot00000000000000GuessIt ======= [![Latest Version](https://img.shields.io/pypi/v/guessit.svg)](https://pypi.python.org/pypi/guessit) [![LGPLv3 License](https://img.shields.io/badge/license-LGPLv3-blue.svg)]() [![Build Status](https://img.shields.io/github/workflow/status/guessit-io/guessit/ci)](https://github.com/guessit-io/guessit/actions?query=workflow%3Aci) [![Coveralls](https://img.shields.io/coveralls/guessit-io/guessit/master.svg)](https://coveralls.io/github/guessit-io/guessit?branch=master) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/relekang/python-semantic-release) GuessIt is a python library that extracts as much information as possible from a video filename. It has a very powerful matcher that allows to guess properties from a video using its filename only. This matcher works with both movies and tv shows episodes. For example, GuessIt can do the following: $ guessit "Treme.1x03.Right.Place,.Wrong.Time.HDTV.XviD-NoTV.avi" For: Treme.1x03.Right.Place,.Wrong.Time.HDTV.XviD-NoTV.avi GuessIt found: { "title": "Treme", "season": 1, "episode": 3, "episode_title": "Right Place, Wrong Time", "source": "HDTV", "video_codec": "Xvid", "release_group": "NoTV", "container": "avi", "mimetype": "video/x-msvideo", "type": "episode" } Migration note ----- In GuessIt 3, some properties and values were renamed in order to keep consistency and to be more intuitive. To migrate from guessit `2.x` to `3.x`, please read [migration2to3.md](./migration2to3.md). To migrate from guessit `0.x` or `1.x` to `guessit 2.x`, please read [migration.md](./migration.md). Install ----- Installing GuessIt is simple with [pip](http://www.pip-installer.org/): ```bash pip install guessit ``` You can also [install GuessIt from sources](./sources.md) Usage ----- GuessIt can be used from command line: ``` usage: guessit [-h] [-t TYPE] [-n] [-Y] [-D] [-L ALLOWED_LANGUAGES] [-C ALLOWED_COUNTRIES] [-E] [-T EXPECTED_TITLE] [-G EXPECTED_GROUP] [--includes INCLUDES] [--excludes EXCLUDES] [-f INPUT_FILE] [-v] [-P SHOW_PROPERTY] [-a] [-s] [-l] [-j] [-y] [-i] [-c CONFIG] [--no-user-config] [--no-default-config] [-p] [-V] [--version] [filename [filename ...]] positional arguments: filename Filename or release name to guess optional arguments: -h, --help show this help message and exit Naming: -t TYPE, --type TYPE The suggested file type: movie, episode. If undefined, type will be guessed. -n, --name-only Parse files as name only, considering "/" and "\" like other separators. -Y, --date-year-first If short date is found, consider the first digits as the year. -D, --date-day-first If short date is found, consider the second digits as the day. -L ALLOWED_LANGUAGES, --allowed-languages ALLOWED_LANGUAGES Allowed language (can be used multiple times) -C ALLOWED_COUNTRIES, --allowed-countries ALLOWED_COUNTRIES Allowed country (can be used multiple times) -E, --episode-prefer-number Guess "serie.213.avi" as the episode 213. Without this option, it will be guessed as season 2, episode 13 -T EXPECTED_TITLE, --expected-title EXPECTED_TITLE Expected title to parse (can be used multiple times) -G EXPECTED_GROUP, --expected-group EXPECTED_GROUP Expected release group (can be used multiple times) --includes INCLUDES List of properties to be detected --excludes EXCLUDES List of properties to be ignored Input: -f INPUT_FILE, --input-file INPUT_FILE Read filenames from an input text file. File should use UTF-8 charset. Output: -v, --verbose Display debug output -P SHOW_PROPERTY, --show-property SHOW_PROPERTY Display the value of a single property (title, series, video_codec, year, ...) -a, --advanced Display advanced information for filename guesses, as json output -s, --single-value Keep only first value found for each property -l, --enforce-list Wrap each found value in a list even when property has a single value -j, --json Display information for filename guesses as json output -y, --yaml Display information for filename guesses as yaml output -i, --output-input-string Add input_string property in the output Configuration: -c CONFIG, --config CONFIG Filepath to configuration file. Configuration file contains the same options as those from command line options, but option names have "-" characters replaced with "_". This configuration will be merged with default and user configuration files. --no-user-config Disable user configuration. If not defined, guessit tries to read configuration files at ~/.guessit/options.(json|yml|yaml) and ~/.config/guessit/options.(json|yml|yaml) --no-default-config Disable default configuration. This should be done only if you are providing a full configuration through user configuration or --config option. If no "advanced_config" is provided by another configuration file, it will still be loaded from default configuration. Information: -p, --properties Display properties that can be guessed. -V, --values Display property values that can be guessed. --version Display the guessit version. ``` It can also be used as a python module: >>> from guessit import guessit >>> guessit('Treme.1x03.Right.Place,.Wrong.Time.HDTV.XviD-NoTV.avi') MatchesDict([('title', 'Treme'), ('season', 1), ('episode', 3), ('episode_title', 'Right Place, Wrong Time'), ('source', 'HDTV'), ('video_codec', 'Xvid'), ('release_group', 'NoTV'), ('container', 'avi'), ('mimetype', 'video/x-msvideo'), ('type', 'episode')]) `MatchesDict` is a dict that keeps matches ordering. Command line options can be given as dict or string to the second argument. Configuration ------------- Find more about Guessit configuration at [configuration page](./configuration.md). REST API -------- A REST API will be available soon ... Sources are available in a dedicated [guessit-rest repository](https://github.com/Toilal/guessit-rest). Support ------- This project is hosted on [GitHub](https://github.com/guessit-io/guessit). Feel free to open an issue if you think you have found a bug or something is missing in guessit. GuessIt relies on [Rebulk](https://github.com/Toilal/rebulk) project for pattern and rules registration. License ------- GuessIt is licensed under the [LGPLv3 license](http://www.gnu.org/licenses/lgpl.html). guessit-3.8.0/docs/migration.md000066400000000000000000000115641453655371700164770ustar00rootroot00000000000000Migration ========= Guessit 2 has been rewritten from scratch. You can find in this file all information required to perform a migration from previous version `0.x` or `1.x`. API --- `guess_video_info`, `guess_movie_info` and `guess_episode_info` have been removed in favor of a unique function `guessit`. Example: >>> from guessit import guessit >>> guessit('Treme.1x03.Right.Place,.Wrong.Time.HDTV.XviD-NoTV.avi') MatchesDict([('title', 'Treme'), ('season', 1), ('episode', 3), ('episode_title', 'Right Place, Wrong Time'), ('format', 'HDTV'), ('video_codec', 'XviD'), ('release_group', 'NoTV'), ('container', 'avi'), ('mimetype', 'video/x-msvideo'), ('type', 'episode')]) `MatchesDict` is a dict that keeps matches ordering. Command line options can be given as dict or string to the second argument. Properties ---------- Some properties have been renamed. - `series` is now `title`. - `title` is now `episode_title` (for `episode` `type` only). - `episodeNumber` is now `episode`. - `bonusNumber` is now `bonus` - `filmNumber` is now `film` - `cdNumber` is now `cd ` and `cdNumberTotal` is now `cd_count` - `idNumber` is now `uuid` `episodeList` and `partList` have been removed. `episode_number` and `part` properties that can now contains an `int` or a `list[int]`. All info `type`, like `seriesinfo` and `movieinfo`. You can check directly `nfo` value in `container` property. All `camelCase` properties have been renamed to `underscore_case`. - `releaseGroup` is now `release_group` - `episodeCount` is now `episode_count` - `episodeDetails` is now `episode_details` - `episodeFormat` is now `episode_format` - `screenSize` is now `screen_size` - `videoCodec` is now `video_codec` - `videoProfile` is now `video_profile` - `videoApi` is now `video_api` - `audioChannels` is now `audio_channels` - `audioCodec` is now `audio_codec` - `audioProfile` is now `audio_profile` - `subtitleLanguage` is now `subtitle_language` - `bonusTitle` is now `bonus_title` - `properCount` is now `proper_count` Options ------- Some options have been removed. - `-X DISABLED_TRANSFORMERS`, `-s, --transformers` There's no transformer anymore. - `-S EXPECTED_SERIES` As `series` was renamed to `title`, use `-T EXPECTED_TITLE` instead. - `-G EXPECTED_GROUP` GuessIt is now better to guess release group, so this option has been removed. - `-d, --demo` Probably not that useful. - `-i INFO, --info INFO` Features related to this option have been removed. - `-c, --split-camel`, `-u, --unidentified`, `-b, --bug` Will be back soon... (work in progress) Other GuessIt `1.x` options have been kept. Output ------ Output produced by `guessit` api function is now an instance of [OrderedDict](https://docs.python.org/2/library/collections.html#collections.OrderedDict). Property values are automatically ordered based on filename, and you can still use this output as a default python `dict`. If multiple values are available for a property, value in the dict will be a `list` instance. `country` 2-letter code is not added to the title anymore. As `country` is added to the returned guess dict, it's up to the user to edit the guessed title. Advanced display option (`-a, --advanced`) output is also changed. It now list `Match` objects from [Rebulk](https://github.com/Toilal/rebulk), and may display duplicates that would have been merged in standard output.: $ guessit "Treme.1x03.Right.Place,.Wrong.Time.HDTV.XviD-NoTV.avi" -a For: Treme.1x03.Right.Place,.Wrong.Time.HDTV.XviD-NoTV.avi GuessIt found: { "title": { "value": "Treme", "raw": "Treme.", "start": 0, "end": 6 }, "season": { "value": 1, "raw": "1", "start": 6, "end": 7 }, "episode": { "value": 3, "raw": "03", "start": 8, "end": 10 }, "episode_title": { "value": "Right Place, Wrong Time", "raw": ".Right.Place,.Wrong.Time.", "start": 10, "end": 35 }, "format": { "value": "HDTV", "raw": "HDTV", "start": 35, "end": 39 }, "video_codec": { "value": "XviD", "raw": "XviD", "start": 40, "end": 44 }, "release_group": { "value": "NoTV", "raw": "-NoTV", "start": 44, "end": 49 }, "container": { "value": "avi", "raw": ".avi", "start": 49, "end": 53 }, "mimetype": { "value": "video/x-msvideo", "start": 53, "end": 53 }, "type": { "value": "episode", "start": 53, "end": 53 } } guessit-3.8.0/docs/migration2to3.md000066400000000000000000000067161453655371700172120ustar00rootroot00000000000000Migration ========= Guessit 3 has introduced breaking changes from previous versions. You can find in this file all information required to perform a migration from previous version `2.x`. API --- No changes. Properties ---------- Some properties have been renamed. - `format` is now `source`. Values ------ The major changes in GuessIt 3 are around the values. Values were renamed in order to keep consistency and to be more intuitive. Acronyms are uppercase (e.g.: `HDTV`). Names follow the official name (e.g.: `Blu-ray`). Words have only the first letter capitalized (e.g.: `Camera`) except prepositions (e.g.: `on`) which are all lowercase. The following values were changed: ### `source` (former `format` property) - `Cam` is now `Camera` or `HD Camera` - `Telesync` is now `Telesync` or `HD Telesync` - `PPV` is now `Pay-per-view` - `DVB` is now `Digital TV` - `VOD` is now `Video on Demand` - `WEBRip` is now `Web` with additional property `other: Rip` - `WEB-DL` is now `Web` - `AHDTV` is now `Analog HDTV` - `UHDTV` is now `Ultra HDTV` - `HDTC` is now `HD Telecine` ### `screen_size` - `360i` was added. - `480i` was added. - `576i` was added. - `900i` was added. - `4K` is now `2160p` - `4320p` was added. ### `video_codec` - `h264` is now `H.264` - `h265` is now `H.265` - `Mpeg2` is now `MPEG-2` - `Real` is now `RealVideo` - `XviD` is now `Xvid` ### `video_profile` - `BP` is now `Baseline`. - `HP` is now `High`. - `XP` is now `Extended`. - `MP` is now `Main`. - `Hi422P` is now `High 4:2:2`. - `Hi444PP` is now `High 4:4:4 Predictive`. - `High 10` was added. - `8bit` was removed. `8bit` is detected as `color_depth: 8-bit` - `10bit` was removed. `10bit` is detected as `color_depth: 10-bit` ### `audio_codec` - `DTS-HD` was added. - `AC3` is now `Dolby Digital` - `EAC3` is now `Dolby Digital Plus` - `TrueHD` is now `Dolby TrueHD` - `DolbyAtmos` is now `Dolby Atmos`. ### `audio_profile` - `HE` is now `High Efficiency`. - `LC` is now `Low Complexity`. - `HQ` is now `High Quality`. - `HDMA` is now `Master Audio`. ### `edition` - `Collector Edition` is now `Collector` - `Special Edition` is now `Special` - `Criterion Edition` is now `Criterion` - `Deluxe Edition` is now `Deluxe` - `Limited Edition` is now `Limited` - `Theatrical Edition` is now `Theatrical` - `Director's Definitive Cut` was added. ### `episode_details` - `Oav` and `Ova` were removed. They are now `other: Original Animated Video` - `Omake` is now `Extras` - `Final` was added. ### `other` - `Rip` was added. E.g.: `DVDRip` will output `other: Rip` - `DDC` was removed. `DDC` is now `edition: Director's Definitive Cut` - `CC` was removed. `CC` is now `edition: Criterion` - `FINAL` was removed. `FINAL` is now `episode_details: Final` - `Original Animated Video` was added. - `OV` is now `Original Video` - `AudioFix` is now `Audio Fixed` - `SyncFix` is now `Sync Fixed` - `DualAudio` is now `Dual Audio` - `Fansub` is now `Fan Subtitled` - `Fastsub` is now `Fast Subtitled` - `FullHD` is now `Full HD` - `UltraHD` is now `Ultra HD` - `mHD` and `HDLight` are now `Micro HD` - `HQ` is now `High Quality` - `HR` is now `High Resolution` - `LD` is now `Line Dubbed` - `MD` is now `Mic Dubbed` - `Low Definition` was added. - `LiNE` is now `Line Audio` - `R5` is now `Region 5` - `Region C` was added. - `ReEncoded` is now `Reencoded` - `WideScreen` is now `Widescreen` guessit-3.8.0/docs/properties.md000066400000000000000000000201631453655371700166750ustar00rootroot00000000000000Properties ========== Guessed values are cleaned up and given in a readable format which may not match exactly the raw filename. So, for instance, - `DVDSCR` will be guessed as `source` = `DVD` + `other` = `Screener` - `1920x1080` will be guessed as `screen_size` = `1080p` - `DD5.1` will be guessed as `audio_codec` = `Dolby Digital` + `audio_channels` = `5.1` Main properties --------------- - **type** Type of the file. - `episode`, `movie` - **title** Title of movie or episode. - **alternative\_title** Other titles found for movie. - **container** Container of the file. - `3g2`, `3gp`, `3gp2`, `asf`, `ass`, `avi`, `divx`, `flv`, `idx`, `iso`, `m4v`, `mk2`, `mk3d`, `mkv`, `mka`, `mov`, `mp4`, `mp4a`, `mpeg`, `mpg`, `nfo`, `nzb`, `ogg`, `ogm`, `ogv`, `qt`, `ra`, `ram`, `rm`, `srt`, `ssa`, `sub`, `torrent`, `ts`, `vob`, `wav`, `webm`, `wma`, `wmv` - **mimetype** Mime type of the related container. Guessed values may vary based on OS native support of mime type. - **date** Date found in filename. - **year** Year of movie (or episode). - **week** Week number, from 1 to 52, of episode. - **release\_group** Name of (non)scene group that released the file. - **website** Name of website contained in the filename. - **streaming\_service** Name of the streaming service. - `A&E`, `ABC`, `ABC Australia`, `Adult Swim`, `Al Jazeera English`, `AMC`, `America's Test Kitchen`, `Amazon Prime`, `Animal Planet`, `AnimeLab`, `AOL`, `AppleTV`, `ARD`, `BBC iPlayer`, `BravoTV`, `Canal+`, `Cartoon Network`, `CBC`, `CBS`, `Channel 4`, `CHRGD`, `Cinemax`, `CNBC`, `Comedy Central`, `Comedians in Cars Getting Coffee`, `Country Music Television`, `Crackle`, `Crunchy Roll`, `CSpan`, `CTV`, `CuriosityStream`, `CWSeed`, `Daisuki`, `DC Universe`, `Deadhouse Films`, `DramaFever`, `Digiturk Diledigin Yerde`, `Discovery`, `DIY Network`, `Disney`, `Doc Club`, `DPlay`, `E!`, `ePix`, `El Trece`, `ESPN`, `Esquire`, `Family`, `Family Jr`, `Food Network`, `Fox`, `Freeform`, `FYI Network`, `Global`, `GloboSat Play`, `Hallmark`, `HBO Go`, `HBO Max`, `HGTV`, `History`, `Hulu`, `Investigation Discovery`, `IFC`, `iTunes`, `ITV`, `Knowledge Network`, `Lifetime`, `Motor Trend OnDemand`, `MBC`, `MSNBC`, `MTV`, `National Geographic`, `NBA TV`, `NBC`, `Netflix`, `NFL`, `NFL Now`, `NHL GameCenter`, `Nickelodeon`, `Norsk Rikskringkasting`, `OnDemandKorea`, `PBS`, `PBS Kids`, `Playstation Network`, `Pluzz`, `RTE One`, `SBS (AU)`, `SeeSo`, `Shomi`, `Showtime`, `Spike`, `Spike TV`, `Sportsnet`, `Sprout`, `Stan`, `Starz`, `Sveriges Television`, `SwearNet`, `Syfy`, `TBS`, `TFou`, `The CW`, `TLC`, `TubiTV`, `TV3 Ireland`, `TV4 Sweeden`, `TVING`, `TV Land`, `UFC`, `UKTV`, `Univision`, `USA Network`, `Velocity`, `VH1`, `Viceland`, `Viki`, `Vimeo`, `VRV`, `W Network`, `WatchMe`, `WWE Network`, `Xbox Video`, `Yahoo`, `YouTube Red`, `ZDF` Episode properties ------------------ - **season** Season number. (Can be a list if several are found) - **episode** Episode number. (Can be a list if several are found) - **disc** Disc number. (Can be a list if several are found) - **episode\_count** Total number of episodes. - **season\_count** Total number of seasons. - **episode\_details** Some details about the episode. - `Final`, `Pilot`, `Special`, `Unaired` - **episode\_format** Episode format of the series. - `Minisode` - **part** Part number of the video. (Can be a list if several are found) - **version** Version of the episode. - In anime fansub scene, new versions are released with tag `v[0-9]`. Video properties ---------------- - **source** Source of the release - `Analog HDTV`, `Blu-ray`, `Camera`, `Digital Master`, `Digital TV`, `DVD`, `HD Camera`, `HD Telecine`, `HD Telesync`, `HD-DVD`, `HDTV`, `Pay-per-view`, `Satellite`, `Telecine`, `Telesync`, `TV`, `Ultra HD Blu-ray`, `Ultra HDTV`, `VHS`, `Video on Demand`, `Web`, `Workprint` - **screen\_size** Resolution of video. - `x`, `360i`, `360p`, `368p`, `480i`, `480p`, `540i`, `540p`, `576i`, `576p`, `720p`, `900i`, `900p`, `1080i`, `1080p`, `1440p`, `2160p`, `4320p` - **aspect\_ratio** Aspect ratio of video. Calculated using width and height from `screen_size` - **video\_codec** Codec used for video. - `DivX`, `H.263`, `H.264`, `H.265`, `MPEG-2`, `RealVideo`, `VP7`, `VP8`, `VP9`,`Xvid` - **video\_profile** Codec profile used for video. - `Baseline`, `High`, `High 10`, `High 4:2:2`, `High 4:4:4 Predictive`, `Main`, `Extended`, `Scalable Video Coding`, `Advanced Video Codec High Definition`, `High Efficiency Video Coding` - **color\_depth** Bit depth used for video. - `8-bit`, `10-bit`, `12-bit` - **video\_api** API used for the video. - `DXVA` - **video\_bit\_rate** Video bit rate (Mbps). Examples: `25Mbps` (``), `40Mbps` (``). - `[]` (object has `magnitude` and `units`) - **frame\_rate** Video frame rate (frames per second). Examples: `25fps` (``), `60fps` (``). - `[]` (object has `magnitude` and `units`) Audio properties ---------------- - **audio\_channels** Number of channels for audio. - `1.0`, `2.0`, `5.1`, `7.1` - **audio\_codec** Codec used for audio. - `AAC`, `Dolby Atmos`, `Dolby Digital`, `Dolby Digital Plus`, `Dolby TrueHD`, `DTS`, `FLAC`, `LPCM`, `MP2`, `MP3`, `Opus`, `PCM`, `Vorbis` - **audio\_profile** The codec profile used for audio. - `Extended Surround`, `EX`, `High Efficiency`, `High Quality`, `High Resolution Audio`, `Low Complexity`, `Master Audio` - **audio\_bit\_rate** Audio bit rate (Kbps, Mbps). Examples: `448Kbps` (``), `1.5Mbps` (``). - `[]` (object has `magnitude` and `units`) Localization properties ----------------------- - **country** Country(ies) of content. Often found in series, `Shameless (US)` for instance. - `[]` (This class equals name and iso code) - **language** Language(s) of the audio soundtrack. - `[]` (This class equals name and iso code) - **subtitle\_language** Language(s) of the subtitles. - `[]` (This class equals name and iso code) Other properties ---------------- - **bonus** Bonus number. - **bonus\_title** Bonus title. - **cd** CD number. - **cd\_count** Total count of CD. - **crc32** CRC32 of the file. - **uuid** Volume identifier (UUID). - **size** Size (MB, GB, TB). Examples: `1.2GB` (``), `430MB` (``). - `[]` (object has `magnitude` and `units`) - **edition** Edition of the movie. - `Alternative Cut`, `Collector`, `Criterion`, `Deluxe`, `Director's Cut`, `Director's Definitive Cut`, `Extended`, `Fan`, `Festival`, `IMAX`, `Remastered`, `Restored`, `Special`, `Limited`, `Theatrical`, `Ultimate`, `Uncensored`, `Uncut`, `Unrated` - **film** Film number of this movie. - **film\_title** Film title of this movie. - **film\_series** Film series of this movie. - **other** Other property will appear under this property. - `2in1`, `3D`, `Audio Fixed`, `Bonus`, `BT.2020`, `Classic`, `Colorized`, `Complete`, `Converted`, `Documentary`, `Dolby Vision`, `Dual Audio`, `East Coast Feed`, `Extras`, `Fan Subtitled`, `Fast Subtitled`, `Full HD`, `Hardcoded Subtitles`, `HD`, `HDR10`, `High Frame Rate`, `Hybrid`, `Variable Frame Rate`, `High Quality`, `High Resolution`, `Internal`, `Line Dubbed`, `Line Audio`, `Mic Dubbed`, `Micro HD`, `Mux`, `NTSC`, `Obfuscated`, `Open Matte`, `Original Aspect Ratio`, `Original Video`, `PAL`, `Preair`, `Proof`, `Proper`, `PS Vita`, `Read NFO`, `Region 5`, `Region C`, `Reencoded`, `Remux`, `Repost`, `Retail`, `Rip`, `Sample`, `Screener`, `SECAM`, `Standard Dynamic Range`, `Straight to Video`, `Sync Fixed`, `Trailer`, `Ultra HD`, `Upscaled`, `West Coast Feed`, `Widescreen`, `XXX` guessit-3.8.0/docs/sources.md000066400000000000000000000014621453655371700161650ustar00rootroot00000000000000Getting the source code ======================= GuessIt is actively developed on [GitHub](https://github.com/guessit-io/guessit). You can either clone the public repository: $ git clone https://github.com/guessit-io/guessit.git Download the [tarball](https://github.com/guessit-io/guessit/tarball/master): $ curl -L https://github.com/guessit-io/guessit/tarball/master -o guessit.tar.gz Or download the [zipball](https://github.com/guessit-io/guessit/zipball/master): $ curl -L https://github.com/guessit-io/guessit/zipball/master -o guessit.zip Once you have a copy of the source, you can embed it in your Python package, install it into your site-packages folder like that: $ python setup.py install or use it directly from the source folder for development: $ python setup.py develop guessit-3.8.0/guessit.spec000066400000000000000000000024011453655371700155610ustar00rootroot00000000000000# -*- mode: python -*- block_cipher = None import babelfish a = Analysis(['guessit/__main__.py'], pathex=[], binaries=[], datas=[ ('guessit/config/*', 'guessit/config'), ('guessit/data/*', 'guessit/data'), (babelfish.__path__[0] + '/data', 'babelfish/data') ], hiddenimports=[ 'pkg_resources.py2_warn', # https://github.com/pypa/setuptools/issues/1963 'babelfish.converters.alpha2', 'babelfish.converters.alpha3b', 'babelfish.converters.alpha3t', 'babelfish.converters.name', 'babelfish.converters.opensubtitles', 'babelfish.converters.countryname' ], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, cipher=block_cipher) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, name='guessit', debug=False, strip=False, upx=False, runtime_tmpdir=None, console=True ) guessit-3.8.0/guessit/000077500000000000000000000000001453655371700147105ustar00rootroot00000000000000guessit-3.8.0/guessit/__init__.py000066400000000000000000000005371453655371700170260ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Extracts as much information as possible from a video file. """ from . import monkeypatch as _monkeypatch from .api import guessit, GuessItApi from .options import ConfigurationException from .rules.common.quantity import Size from .__version__ import __version__ _monkeypatch.monkeypatch_rebulk() guessit-3.8.0/guessit/__main__.py000066400000000000000000000122711453655371700170050ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Entry point module """ # pragma: no cover import json import logging import sys from collections import OrderedDict from rebulk.__version__ import __version__ as __rebulk_version__ from guessit import api from guessit.__version__ import __version__ from guessit.jsonutils import GuessitEncoder from guessit.options import argument_parser, parse_options, load_config, merge_options def guess_filename(filename, options): """ Guess a single filename using given options :param filename: filename to parse :type filename: str :param options: :type options: dict :return: :rtype: """ if not options.get('yaml') and not options.get('json') and not options.get('show_property'): print('For:', filename) guess = api.guessit(filename, options) if options.get('show_property'): print(guess.get(options.get('show_property'), '')) return if options.get('json'): print(json.dumps(guess, cls=GuessitEncoder, ensure_ascii=False)) elif options.get('yaml'): # pylint:disable=import-outside-toplevel import yaml from guessit import yamlutils ystr = yaml.dump({filename: OrderedDict(guess)}, Dumper=yamlutils.CustomDumper, default_flow_style=False, allow_unicode=True) i = 0 for yline in ystr.splitlines(): if i == 0: print("? " + yline[:-1]) elif i == 1: print(":" + yline[1:]) else: print(yline) i += 1 else: print('GuessIt found:', json.dumps(guess, cls=GuessitEncoder, indent=4, ensure_ascii=False)) def display_properties(options): """ Display properties """ properties = api.properties(options) if options.get('json'): if options.get('values'): print(json.dumps(properties, cls=GuessitEncoder, ensure_ascii=False)) else: print(json.dumps(list(properties.keys()), cls=GuessitEncoder, ensure_ascii=False)) elif options.get('yaml'): # pylint:disable=import-outside-toplevel import yaml from guessit import yamlutils if options.get('values'): print(yaml.dump(properties, Dumper=yamlutils.CustomDumper, default_flow_style=False, allow_unicode=True)) else: print(yaml.dump(list(properties.keys()), Dumper=yamlutils.CustomDumper, default_flow_style=False, allow_unicode=True)) else: print('GuessIt properties:') properties_list = list(sorted(properties.keys())) for property_name in properties_list: property_values = properties.get(property_name) print(2 * ' ' + f'[+] {property_name}') if property_values and options.get('values'): for property_value in property_values: print(4 * ' ' + f'[!] {property_value}') def main(args=None): # pylint:disable=too-many-branches """ Main function for entry point """ if args is None: # pragma: no cover options = parse_options() else: options = parse_options(args) config = load_config(options) options = merge_options(config, options) if options.get('verbose'): logging.basicConfig(stream=sys.stdout, format='%(message)s') logging.getLogger().setLevel(logging.DEBUG) help_required = True if options.get('version'): print('+-------------------------------------------------------+') print('+ GuessIt ' + __version__ + (28 - len(__version__)) * ' ' + '+') print('+-------------------------------------------------------+') print('+ Rebulk ' + __rebulk_version__ + (29 - len(__rebulk_version__)) * ' ' + '+') print('+-------------------------------------------------------+') print('| Please report any bug or feature request at |') print('| https://github.com/guessit-io/guessit/issues. |') print('+-------------------------------------------------------+') help_required = False if options.get('yaml'): try: import yaml # pylint:disable=unused-variable,unused-import,import-outside-toplevel except ImportError: # pragma: no cover del options['yaml'] print('PyYAML is not installed. \'--yaml\' option will be ignored ...', file=sys.stderr) if options.get('properties') or options.get('values'): display_properties(options) help_required = False filenames = [] if options.get('filename'): for filename in options.get('filename'): filenames.append(filename) if options.get('input_file'): with open(options.get('input_file'), 'r', encoding='utf-8') as input_file: filenames.extend([line.strip() for line in input_file.readlines()]) filenames = list(filter(lambda f: f, filenames)) if filenames: for filename in filenames: help_required = False guess_filename(filename, options) if help_required: # pragma: no cover argument_parser.print_help() if __name__ == '__main__': # pragma: no cover main() guessit-3.8.0/guessit/__version__.py000066400000000000000000000001561453655371700175450ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Version module """ # pragma: no cover __version__ = '3.8.0' guessit-3.8.0/guessit/api.py000066400000000000000000000210541453655371700160350ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ API functions that can be used by external software """ import os import traceback from collections import OrderedDict from copy import deepcopy from pathlib import Path from rebulk.introspector import introspect from .__version__ import __version__ from .options import parse_options, load_config, merge_options from .rules import rebulk_builder class GuessitException(Exception): """ Exception raised when guessit fails to perform a guess because of an internal error. """ def __init__(self, string, options): super().__init__("An internal error has occurred in guessit.\n" "===================== Guessit Exception Report =====================\n" f"version={__version__}\n" f"string={str(string)}\n" f"options={str(options)}\n" "--------------------------------------------------------------------\n" f"{traceback.format_exc()}" "--------------------------------------------------------------------\n" "Please report at " "https://github.com/guessit-io/guessit/issues.\n" "====================================================================") self.string = string self.options = options def configure(options=None, rules_builder=None, force=False): """ Load configuration files and initialize rebulk rules if required. :param options: :type options: dict :param rules_builder: :type rules_builder: :param force: :type force: bool :return: """ default_api.configure(options, rules_builder=rules_builder, force=force) def reset(): """ Reset api internal state. """ default_api.reset() def guessit(string, options=None): """ Retrieves all matches from string as a dict :param string: the filename or release name :type string: str :param options: :type options: str|dict :return: :rtype: """ return default_api.guessit(string, options) def properties(options=None): """ Retrieves all properties with possible values that can be guessed :param options: :type options: str|dict :return: :rtype: """ return default_api.properties(options) def suggested_expected(titles, options=None): """ Return a list of suggested titles to be used as `expected_title` based on the list of titles :param titles: the filename or release name :type titles: list|set|dict :param options: :type options: str|dict :return: :rtype: list of str """ return default_api.suggested_expected(titles, options) class GuessItApi: """ An api class that can be configured with custom Rebulk configuration. """ def __init__(self): """Default constructor.""" self.rebulk = None self.config = None self.load_config_options = None self.advanced_config = None def reset(self): """ Reset api internal state. """ self.__init__() # pylint:disable=unnecessary-dunder-call @classmethod def _fix_encoding(cls, value): if isinstance(value, list): return [cls._fix_encoding(item) for item in value] if isinstance(value, dict): return {cls._fix_encoding(k): cls._fix_encoding(v) for k, v in value.items()} if isinstance(value, bytes): return value.decode('ascii') return value @classmethod def _has_same_properties(cls, dic1, dic2, values): for value in values: if dic1.get(value) != dic2.get(value): return False return True def configure(self, options=None, rules_builder=None, force=False, sanitize_options=True): """ Load configuration files and initialize rebulk rules if required. :param options: :type options: str|dict :param rules_builder: :type rules_builder: :param force: :type force: bool :param sanitize_options: :type force: bool :return: :rtype: dict """ if not rules_builder: rules_builder = rebulk_builder if sanitize_options: options = parse_options(options, True) options = self._fix_encoding(options) if self.config is None or self.load_config_options is None or force or \ not self._has_same_properties(self.load_config_options, options, ['config', 'no_user_config', 'no_default_config']): config = load_config(options) config = self._fix_encoding(config) self.load_config_options = options else: config = self.config advanced_config = merge_options(config.get('advanced_config'), options.get('advanced_config')) should_build_rebulk = force or not self.rebulk or not self.advanced_config or \ self.advanced_config != advanced_config if should_build_rebulk: self.advanced_config = deepcopy(advanced_config) self.rebulk = rules_builder(advanced_config) self.config = config return self.config def guessit(self, string, options=None): # pylint: disable=too-many-branches """ Retrieves all matches from string as a dict :param string: the filename or release name :type string: str|Path :param options: :type options: str|dict :return: :rtype: """ if isinstance(string, Path): try: # Handle path-like object string = os.fspath(string) except AttributeError: string = str(string) try: options = parse_options(options, True) options = self._fix_encoding(options) config = self.configure(options, sanitize_options=False) options = merge_options(config, options) result_decode = False result_encode = False if isinstance(string, bytes): string = string.decode('ascii') result_encode = True matches = self.rebulk.matches(string, options) if result_decode: for match in matches: if isinstance(match.value, bytes): match.value = match.value.decode("utf-8") if result_encode: for match in matches: if isinstance(match.value, str): match.value = match.value.encode("ascii") matches_dict = matches.to_dict(options.get('advanced', False), options.get('single_value', False), options.get('enforce_list', False)) output_input_string = options.get('output_input_string', False) if output_input_string: matches_dict['input_string'] = matches.input_string return matches_dict except Exception as err: raise GuessitException(string, options) from err def properties(self, options=None): """ Grab properties and values that can be generated. :param options: :type options: :return: :rtype: """ options = parse_options(options, True) options = self._fix_encoding(options) config = self.configure(options, sanitize_options=False) options = merge_options(config, options) unordered = introspect(self.rebulk, options).properties ordered = OrderedDict() for k in sorted(unordered.keys(), key=str): ordered[k] = list(sorted(unordered[k], key=str)) if hasattr(self.rebulk, 'customize_properties'): ordered = self.rebulk.customize_properties(ordered) return ordered def suggested_expected(self, titles, options=None): """ Return a list of suggested titles to be used as `expected_title` based on the list of titles :param titles: the filename or release name :type titles: list|set|dict :param options: :type options: str|dict :return: :rtype: list of str """ suggested = [] for title in titles: guess = self.guessit(title, options) if len(guess) != 2 or 'title' not in guess: suggested.append(title) return suggested default_api = GuessItApi() guessit-3.8.0/guessit/config/000077500000000000000000000000001453655371700161555ustar00rootroot00000000000000guessit-3.8.0/guessit/config/__init__.py000066400000000000000000000121121453655371700202630ustar00rootroot00000000000000""" Config module. """ from importlib import import_module from typing import Any, List from rebulk import Rebulk _regex_prefix = 're:' _import_prefix = 'import:' _import_cache = {} _eval_prefix = 'eval:' _eval_cache = {} _pattern_types = ('regex', 'string') _default_module_names = { 'validator': 'guessit.rules.common.validators', 'formatter': 'guessit.rules.common.formatters' } def _process_option(name: str, value: Any): if name in ('validator', 'conflict_solver', 'formatter'): if isinstance(value, dict): return {item_key: _process_option(name, item_value) for item_key, item_value in value.items()} if value is not None: return _process_option_executable(value, _default_module_names.get(name)) return value def _import(value: str, default_module_name=None): if '.' in value: module_name, target = value.rsplit(':', 1) else: module_name = default_module_name target = value import_id = module_name + ":" + target if import_id in _import_cache: return _import_cache[import_id] mod = import_module(module_name) imported = mod for item in target.split("."): imported = getattr(imported, item) _import_cache[import_id] = imported return imported def _eval(value: str): compiled = _eval_cache.get(value) if not compiled: compiled = compile(value, '', 'eval') return eval(compiled) # pylint:disable=eval-used def _process_option_executable(value: str, default_module_name=None): if value.startswith(_import_prefix): value = value[len(_import_prefix):] return _import(value, default_module_name) if value.startswith(_eval_prefix): value = value[len(_eval_prefix):] return _eval(value) if value.startswith('lambda ') or value.startswith('lambda:'): return _eval(value) return value def _process_callable_entry(callable_spec: str, rebulk: Rebulk, entry: dict): _process_option_executable(callable_spec)(rebulk, **entry) def _build_entry_decl(entry, options, value): entry_decl = dict(options.get(None, {})) if not value.startswith('_'): entry_decl['value'] = value if isinstance(entry, str): if entry.startswith(_regex_prefix): entry_decl["regex"] = [entry[len(_regex_prefix):]] else: entry_decl["string"] = [entry] else: entry_decl.update(entry) if "pattern" in entry_decl: legacy_pattern = entry.pop("pattern") if legacy_pattern.startswith(_regex_prefix): entry_decl["regex"] = [legacy_pattern[len(_regex_prefix):]] else: entry_decl["string"] = [legacy_pattern] return entry_decl def load_patterns(rebulk: Rebulk, pattern_type: str, patterns: List[str], options: dict = None): """ Load patterns for a prepared config entry :param rebulk: Rebulk builder to use. :param pattern_type: Pattern type. :param patterns: Patterns :param options: kwargs options to pass to rebulk pattern function. :return: """ default_options = options.get(None) if options else None item_options = dict(default_options) if default_options else {} pattern_type_option = options.get(pattern_type) if pattern_type_option: item_options.update(pattern_type_option) item_options = {name: _process_option(name, value) for name, value in item_options.items()} getattr(rebulk, pattern_type)(*patterns, **item_options) def load_config_patterns(rebulk: Rebulk, config: dict, options: dict = None): """ Load patterns defined in given config. :param rebulk: Rebulk builder to use. :param config: dict containing pattern definition. :param options: Additional pattern options to use. :type options: Dict[Dict[str, str]] A dict where key is the pattern type (regex, string, functional) and value is the default kwargs options to pass. :return: """ if options is None: options = {} for value, raw_entries in config.items(): entries = raw_entries if isinstance(raw_entries, list) else [raw_entries] for entry in entries: if isinstance(entry, dict) and "callable" in entry.keys(): _process_callable_entry(entry.pop("callable"), rebulk, entry) continue entry_decl = _build_entry_decl(entry, options, value) for pattern_type in _pattern_types: patterns = entry_decl.get(pattern_type) if not patterns: continue if not isinstance(patterns, list): patterns = [patterns] patterns_entry_decl = dict(entry_decl) for pattern_type_to_remove in _pattern_types: patterns_entry_decl.pop(pattern_type_to_remove, None) current_pattern_options = dict(options) current_pattern_options[None] = patterns_entry_decl load_patterns(rebulk, pattern_type, patterns, current_pattern_options) guessit-3.8.0/guessit/config/options.json000066400000000000000000000570441453655371700205550ustar00rootroot00000000000000{ "expected_title": [ "OSS 117", "This is Us" ], "allowed_countries": [ "au", "gb", "us" ], "allowed_languages": [ "ca", "cs", "de", "en", "es", "fr", "he", "hi", "hu", "it", "ja", "ko", "mul", "nl", "no", "pl", "pt", "ro", "ru", "sv", "te", "uk", "und" ], "advanced_config": { "common_words": [ "ca", "cat", "de", "he", "it", "no", "por", "rum", "se", "st", "sub" ], "groups": { "starting": "([{", "ending": ")]}" }, "audio_codec": { "audio_codec": { "MP3": {"string": ["MP3", "LAME"],"regex": ["LAME(?:\\d)+-?(?:\\d)+"]}, "MP2": "MP2", "Dolby Digital": {"string": ["Dolby", "DolbyDigital"], "regex": ["Dolby-Digital", "DD", "AC-?3D?"]}, "Dolby Atmos": {"string": ["Atmos"], "regex": ["Dolby-?Atmos"]}, "AAC": "AAC", "Dolby Digital Plus": {"string": ["DDP", "DD+"], "regex": ["E-?AC-?3"]}, "FLAC": "Flac", "DTS": "DTS", "DTS-HD": {"regex": ["DTS-?HD", "DTS(?=-?MA)"], "conflict_solver": "lambda match, other: other if other.name == 'audio_codec' else '__default__'"}, "DTS:X": {"string": ["DTS:X", "DTS-X", "DTSX"] }, "Dolby TrueHD": {"regex": ["True-?HD"] }, "Opus": "Opus", "Vorbis": "Vorbis", "PCM": "PCM", "LPCM": "LPCM" }, "audio_channels": { "1.0": [ "1ch", "mono", "re:(1[\\W_]0(?:ch)?)(?=[^\\d]|$)" ], "2.0": [ "2ch", "stereo", {"regex": "(2[\\W_]0(?:ch)?)(?=[^\\d]|$)", "children": true}, {"string": "20", "validator": "import:seps_after", "tags": "weak-audio_channels"} ], "5.1": [ "5ch", "6ch", {"regex": "(5[\\W_][01](?:ch)?)(?=[^\\d]|$)", "children": true}, {"regex": "(6[\\W_]0(?:ch)?)(?=[^\\d]|$)", "children": true}, {"regex": "5[01]", "validator": "import:seps_after", "tags": "weak-audio_channels"} ], "7.1": [ "7ch", "8ch", {"regex": "(7[\\W_][01](?:ch)?)(?=[^\\d]|$)", "children": true}, {"regex": "7[01]", "validator": "import:seps_after", "tags": "weak-audio_channels"} ] }, "audio_profile": { "Master Audio": {"string": "MA", "tags": ["audio_profile.rule", "DTS-HD"]}, "High Resolution Audio": {"string": ["HR", "HRA"], "tags": ["audio_profile.rule", "DTS-HD"]}, "Extended Surround": {"string": "ES", "tags": ["audio_profile.rule", "DTS"]}, "High Efficiency": {"string": "HE", "tags": ["audio_profile.rule", "AAC"]}, "Low Complexity": {"string": "LC", "tags": ["audio_profile.rule", "AAC"]}, "High Quality": {"string": "HQ", "tags": ["audio_profile.rule", "Dolby Digital"]}, "EX": {"string": "EX", "tags": ["audio_profile.rule", "Dolby Digital"]} } }, "bit_rate": { "bit_rate": { "_": { "regex": ["\\d+-?[kmg]b(ps|its?)", "\\d+\\.\\d+-?[kmg]b(ps|its?)"], "conflict_solver": "lambda match, other: match if other.name == 'audio_channels' and 'weak-audio_channels' not in other.tags else other", "formatter": "import:guessit.rules.common.quantity:BitRate.fromstring", "tags": ["release-group-prefix"] } } }, "bonus": { "bonus": { "_": { "regex": "x(\\d+)", "private_parent": true, "children": true, "formatter": "eval:int", "validator": {"__parent__": "import:seps_surround"}, "validate_all": true, "conflict_solver": "lambda match, conflicting: match if conflicting.name in ('video_codec', 'episode') and 'weak-episode' not in conflicting.tags else '__default__'" } } }, "cd": { "_cd_of_cd_count": { "regex": "cd-?(?P\\d+)(?:-?of-?(?P\\d+))?", "validator": { "cd": "lambda match: 0 < match.value < 100", "cd_count": "lambda match: 0 < match.value < 100" }, "formatter": {"cd": "eval:int", "cd_count": "eval:int"}, "children": true, "private_parent": true, "properties": {"cd": [null], "cd_count": [null]} }, "_cd_count": { "regex": "(?P\\d+)-?cds?", "validator": { "cd": "lambda match: 0 < match.value < 100", "cd_count": "lambda match: 0 < match.value < 100" }, "formatter": {"cd_count": "eval:int"}, "children": true, "private_parent": true, "properties": {"cd": [null], "cd_count": [null]} } }, "container": { "subtitles": [ "srt", "idx", "sub", "ssa", "ass" ], "info": [ "nfo" ], "videos": [ "3g2", "3gp", "3gp2", "asf", "avi", "divx", "flv", "iso", "m4v", "mk2", "mk3d", "mka", "mkv", "mov", "mp4", "mp4a", "mpeg", "mpg", "ogg", "ogm", "ogv", "qt", "ra", "ram", "rm", "ts", "m2ts", "vob", "wav", "webm", "wma", "wmv" ], "torrent": [ "torrent" ], "nzb": [ "nzb" ] }, "country": { "synonyms": { "ES": [ "españa" ], "GB": [ "UK" ], "BR": [ "brazilian", "bra" ], "CA": [ "québec", "quebec", "qc" ], "MX": [ "Latinoamérica", "latin america" ] } }, "edition": { "edition": { "Collector": {"string": ["collector"], "regex": ["collector'?s?-edition", "edition-collector"]}, "Special": [ {"regex": ["special-edition", "edition-special"], "conflict_solver": "lambda match, other: other if other.name == 'episode_details' and other.value == 'Special' else '__default__'"}, {"string": "se", "tags": "has-neighbor"} ], "Director's Definitive Cut": "ddc", "Criterion": {"string": ["CC", "Criterion"], "regex": ["criterion-edition", "edition-criterion"] }, "Deluxe": {"string": ["deluxe"], "regex": ["deluxe-edition", "edition-deluxe"] }, "Limited": {"string": ["limited"], "regex": ["limited-edition"], "tags": ["has-neighbor", "release-group-prefix"]}, "Theatrical": {"string": ["theatrical"], "regex": ["theatrical-cut", "theatrical-edition"]}, "Director's Cut": {"string": ["DC"], "regex": ["director'?s?-cut", "director'?s?-cut-edition", "edition-director'?s?-cut"]}, "Extended": {"string": ["extended"], "regex": ["extended-?cut", "extended-?version"], "tags": ["has-neighbor", "release-group-prefix"]}, "Alternative Cut": {"regex": ["alternat(e|ive)(?:-?Cut)?"], "tags": ["has-neighbor", "release-group-prefix"]}, "Remastered": [ {"regex": "(?:4k.)?remaster(?:ed)?", "tags": ["release-group-prefix"]} ], "Restored": [ {"regex": "(?:4k.)?restore(?:d)?", "tags": ["release-group-prefix"]} ], "Uncensored": {"string": "Uncensored", "tags": ["has-neighbor", "release-group-prefix"]}, "Uncut": {"string": "Uncut", "tags": ["has-neighbor", "release-group-prefix"]}, "Unrated": {"string": "Unrated", "tags": ["has-neighbor", "release-group-prefix"]}, "Festival": {"string": "Festival", "tags": ["has-neighbor-before", "has-neighbor-after"]}, "IMAX": {"string": ["imax"], "regex": ["imax-edition"]}, "Fan": {"regex": ["fan-edit(?:ion)?", "fan-collection"]}, "Ultimate": {"regex": ["ultimate-edition"]}, "_Ultimate_Collector": {"regex": ["ultimate-collector'?s?-edition"], "value": ["Ultimate", "Collector"]}, "_Ultimate_Fan": {"regex": ["ultimate-fan-edit(?:ion)?", "ultimate-fan-collection"], "value": ["Ultimate", "Fan"]} } }, "episodes": { "season_max_range": 100, "episode_max_range": 100, "max_range_gap": 1, "season_markers": [ "s" ], "season_ep_markers": [ "x" ], "disc_markers": [ "d" ], "episode_markers": [ "xe", "ex", "ep", "e", "x" ], "range_separators": [ "-", "~", "to", "a" ], "discrete_separators": [ "+", "&", "and", "et" ], "season_words": [ "season", "saison", "seizoen", "seasons", "saisons", "tem", "temp", "temporada", "temporadas", "stagione" ], "episode_words": [ "episode", "episodes", "eps", "ep", "episodio", "episodios", "capitulo", "capitulos" ], "of_words": [ "of", "sur" ], "all_words": [ "All" ] }, "film": { "film": { "_f": {"regex": "f(\\d{1,2})", "name": "film", "validate_all": true, "validator": {"__parent__": "import:seps_surround"}, "private_parent": true, "children": true, "formatter": "eval:int"} } }, "language": { "synonyms": { "ell": [ "gr", "greek" ], "spa": [ "esp", "español", "espanol" ], "fra": [ "français", "vf", "vff", "vfi", "vfq" ], "swe": [ "se" ], "por_BR": [ "po", "pb", "pob", "ptbr", "br", "brazilian" ], "deu_CH": [ "swissgerman", "swiss german" ], "nld_BE": [ "flemish" ], "cat": [ "català", "castellano", "espanol castellano", "español castellano" ], "ces": [ "cz" ], "ukr": [ "ua" ], "zho": [ "cn" ], "jpn": [ "jp" ], "hrv": [ "scr" ], "mul": [ "multi", "multiple", "dl" ] }, "subtitle_affixes": [ "sub", "subs", "subtitle", "subtitles", "esub", "esubs", "subbed", "custom subbed", "custom subs", "custom sub", "customsubbed", "customsubs", "customsub", "soft subtitles", "soft subs" ], "subtitle_prefixes": [ "st", "vost", "subforced", "fansub", "hardsub", "legenda", "legendas", "legendado", "subtitulado", "soft" ], "subtitle_suffixes": [ "subforced", "fansub", "hardsub" ], "language_affixes": [ "dublado", "dubbed", "dub" ], "language_prefixes": [ "true" ], "language_suffixes": [ "audio" ], "weak_affixes": [ "v", "audio", "true" ] }, "other": { "other": { "Audio Fixed": {"regex": ["Audio-?Fix", "Audio-?Fixed"]}, "Sync Fixed": {"regex": ["Sync-?Fix", "Sync-?Fixed"]}, "Dual Audio": {"string": ["Dual"], "regex": ["Dual-?Audio"]}, "Widescreen": {"string": ["ws"], "regex": ["wide-?screen"]}, "Reencoded": {"regex": ["Re-?Enc(?:oded)?"]}, "_repack_with_count": {"regex": ["Repack(?P\\d*)", "Rerip(?P\\d*)"], "value": {"other": "Proper"}, "tags": ["streaming_service.prefix", "streaming_service.suffix"]}, "Proper": [ {"string": "Proper", "tags": ["has-neighbor", "streaming_service.prefix", "streaming_service.suffix"]}, {"regex": ["Real-Proper", "Real-Repack", "Real-Rerip"], "tags": ["streaming_service.prefix", "streaming_service.suffix", "real"]}, {"string": "Real", "tags": ["has-neighbor", "streaming_service.prefix", "streaming_service.suffix", "real"]} ], "Fix": [ {"string": ["Fix", "Fixed"], "tags": ["has-neighbor-before", "has-neighbor-after", "streaming_service.prefix", "streaming_service.suffix"]}, {"string": ["Dirfix", "Nfofix", "Prooffix"], "tags": ["streaming_service.prefix", "streaming_service.suffix"]}, {"regex": ["(?:Proof-?)?Sample-?Fix"], "tags": ["streaming_service.prefix", "streaming_service.suffix"]} ], "Fan Subtitled": {"string": "Fansub", "tags": "has-neighbor"}, "Fast Subtitled": {"string": "Fastsub", "tags": "has-neighbor"}, "Region 5": "R5", "Region C": "RC", "Preair": {"regex": "Pre-?Air"}, "PS Vita": [ {"regex": "(?:PS-?)Vita"}, {"string": "Vita", "tags": "has-neighbor"} ], "_HdRip": {"value": {"other": "HD", "another": "Rip"}, "regex": ["(HD)(?PRip)"], "private_parent": true, "children": true, "validator":{"__parent__": "import:seps_surround"}, "validate_all": true}, "Screener": [ "Screener", {"regex": "Scr(?:eener)?", "validator": null, "tags": ["other.validate.screener", "source-prefix", "source-suffix"]} ], "Remux": "Remux", "Hybrid": "Hybrid", "PAL": "PAL", "SECAM": "SECAM", "NTSC": "NTSC", "XXX": "XXX", "2in1": "2in1", "3D": {"string": "3D", "tags": "has-neighbor"}, "High Quality": {"string": "HQ", "tags": "uhdbluray-neighbor"}, "High Resolution": "HR", "Line Dubbed": "LD", "Mic Dubbed": "MD", "Micro HD": ["mHD", "HDLight"], "Low Definition": "LDTV", "High Frame Rate": "HFR", "Variable Frame Rate": "VFR", "HD": {"string": "HD", "validator": null, "tags": ["streaming_service.prefix", "streaming_service.suffix"]}, "Full HD": {"string": ["FHD"],"regex": ["Full-?HD"], "validator": null, "tags": ["streaming_service.prefix", "streaming_service.suffix"]}, "Ultra HD": {"string": ["UHD"],"regex": ["Ultra-?(?:HD)?"], "validator": null, "tags": ["streaming_service.prefix", "streaming_service.suffix"]}, "Upscaled": {"regex": "Upscaled?"}, "Complete": {"string": ["Complet", "Complete"], "tags": ["has-neighbor", "release-group-prefix"]}, "Classic": {"string": "Classic", "tags": ["has-neighbor", "release-group-prefix"]}, "Bonus": {"string": "Bonus", "tags": ["has-neighbor", "release-group-prefix"]}, "Trailer": {"string": "Trailer", "tags": ["has-neighbor", "release-group-prefix"]}, "Retail": {"string": "Retail", "tags": ["has-neighbor", "release-group-prefix"]}, "Colorized": {"string": "Colorized", "tags": ["has-neighbor", "release-group-prefix"]}, "Internal": {"string": "Internal", "tags": ["has-neighbor", "release-group-prefix"]}, "Line Audio": {"string": "LiNE", "tags": ["has-neighbor-before", "has-neighbor-after", "release-group-prefix"]}, "Read NFO": {"regex": "Read-?NFO"}, "Converted": {"string": "CONVERT", "tags": "has-neighbor"}, "Documentary": {"string": ["DOCU", "DOKU"], "tags": "has-neighbor"}, "Open Matte": {"regex": "(?:OM|Open-?Matte)", "tags": "has-neighbor"}, "Straight to Video": {"string": "STV", "tags": "has-neighbor"}, "Original Aspect Ratio": {"string": "OAR", "tags": "has-neighbor"}, "East Coast Feed": {"regex": "(?:Live-)?(?:Episode-)?East-?(?:Coast-)?Feed"}, "West Coast Feed": {"regex": "(?:Live-)?(?:Episode-)?West-?(?:Coast-)?Feed"}, "Original Video": {"string": ["VO", "OV"], "tags": "has-neighbor"}, "Original Animated Video": {"string": ["Ova", "Oav"]}, "Original Net Animation": "Ona", "Original Animation DVD": "Oad", "Mux": {"string": "Mux", "validator": "import:seps_after", "tags": ["other.validate.mux", "video-codec-prefix", "source-suffix"]}, "Hardcoded Subtitles": ["HC", "vost"], "Standard Dynamic Range": {"string": "SDR", "tags": "uhdbluray-neighbor"}, "HDR10": {"regex": "HDR(?:10)?", "tags": "uhdbluray-neighbor"}, "Dolby Vision": {"regex": "(?:Dolby-?Vision|DV)", "tags": "uhdbluray-neighbor"}, "BT.2020": {"regex": "BT-?2020","tags": "uhdbluray-neighbor"}, "Sample": {"string": "Sample", "tags": ["at-end", "not-a-release-group"]}, "Extras": [ {"string": "Extras", "tags": "has-neighbor"}, {"regex": "Digital-?Extras?"} ], "Proof": {"string": "Proof", "tags": ["at-end", "not-a-release-group"]}, "Obfuscated": {"string": ["Obfuscated", "Scrambled"], "tags": ["at-end", "not-a-release-group"]}, "Repost": {"string": ["xpost", "postbot", "asrequested"], "tags": "not-a-release-group"}, "_complete_words": {"callable": "import:guessit.rules.properties.other:complete_words", "season_words": ["seasons?", "series?"], "complete_article_words": ["The"]} } }, "part": { "prefixes": [ "pt", "part" ] }, "release_group": { "forbidden_names": [ "bonus", "by", "for", "par", "pour", "rip" ], "ignored_seps": "[]{}()" }, "screen_size": { "frame_rates": [ "23\\.976", "24(?:\\.0{1,3})?", "25(?:\\.0{1,3})?", "29\\.970", "30(?:\\.0{1,3})?", "48(?:\\.0{1,3})?", "50(?:\\.0{1,3})?", "60(?:\\.0{1,3})?", "120(?:\\.0{1,3})?" ], "min_ar": 1.333, "max_ar": 1.898, "interlaced": [ "360", "480", "540", "576", "900", "1080" ], "progressive": [ "360", "480", "540", "576", "900", "1080", "368", "720", "1440", "2160", "4320" ] }, "source": { "rip_prefix": "(?PRip)-?", "rip_suffix": "-?(?PRip)" }, "website": { "safe_tlds": [ "com", "net", "org" ], "safe_subdomains": [ "www" ], "safe_prefixes": [ "co", "com", "net", "org" ], "prefixes": [ "from" ] }, "streaming_service": { "9Now": "9NOW", "A&E": [ "AE", "A&E" ], "ABC": "AMBC", "ABC Australia": "AUBC", "Al Jazeera English": "AJAZ", "AMC": "AMC", "Amazon Prime": [ "AMZN", "AMZN-CBR", "Amazon", "re:Amazon-?Prime" ], "Adult Swim": [ "AS", "re:Adult-?Swim" ], "America's Test Kitchen": "ATK", "Animal Planet": "ANPL", "AnimeLab": "ANLB", "AOL": "AOL", "AppleTV": [ "ATVP", "ATV+", "APTV" ], "ARD": "ARD", "BBC iPlayer": [ "iP", "re:BBC-?iPlayer" ], "Binge": "BNGE", "Blackpills": "BKPL", "BluTV": "BLU", "Boomerang": "BOOM", "Disney+": "DSNP", "BravoTV": "BRAV", "Canal+": "CNLP", "Cartoon Network": "CN", "CBC": "CBC", "CBS": "CBS", "CNBC": "CNBC", "Comedy Central": [ "CC", "re:Comedy-?Central" ], "Channel 4": [ "ALL4", "4OD" ], "CHRGD": "CHGD", "Cinemax": "CMAX", "Country Music Television": "CMT", "Comedians in Cars Getting Coffee": "CCGC", "Crave": "CRAV", "Crunchy Roll": [ "CR", "re:Crunchy-?Roll" ], "Crackle": "CRKL", "CSpan": "CSPN", "CTV": "CTV", "CuriosityStream": "CUR", "CWSeed": "CWS", "Daisuki": "DSKI", "DC Universe": "DCU", "Deadhouse Films": "DHF", "DramaFever": [ "DF", "DramaFever" ], "Digiturk Diledigin Yerde": "DDY", "Discovery": [ "DISC", "Discovery" ], "Discovery Plus": "DSCP", "Disney": [ "DSNY", "Disney" ], "DIY Network": "DIY", "Doc Club": "DOCC", "DPlay": "DPLY", "E!": "ETV", "ePix": "EPIX", "El Trece": "ETTV", "ESPN": "ESPN", "Esquire": "ESQ", "Facebook Watch": "FBWatch", "Family": "FAM", "Family Jr": "FJR", "Fandor": "FANDOR", "Food Network": "FOOD", "Fox": "FOX", "Fox Premium": "FOXP", "Foxtel": "FXTL", "Freeform": "FREE", "FYI Network": "FYI", "GagaOOLala": "Gaga", "Global": "GLBL", "GloboSat Play": "GLOB", "Hallmark": "HLMK", "HBO Go": [ "HBO", "re:HBO-?Go" ], "HBO Max": "HMAX", "HGTV": "HGTV", "History": [ "HIST", "History" ], "Hulu": "HULU", "Investigation Discovery": "ID", "IFC": "IFC", "hoichoi": "HoiChoi", "iflix": "IFX", "iQIYI": "iQIYI", "iTunes": [ "iTunes", {"pattern": "iT", "ignore_case": false} ], "ITV": "ITV", "Knowledge Network": "KNOW", "Lifetime": "LIFE", "Motor Trend OnDemand": "MTOD", "MBC": [ "MBC", "MBCVOD" ], "MSNBC": "MNBC", "MTV": "MTV", "MUBI": "MUBI", "National Audiovisual Institute": "INA", "National Film Board": "NFB", "National Geographic": [ "NATG", "re:National-?Geographic" ], "NBA TV": [ "NBA", "re:NBA-?TV" ], "NBC": "NBC", "Netflix": [ "NF", "Netflix" ], "NFL": "NFL", "NFL Now": "NFLN", "NHL GameCenter": "GC", "Nickelodeon": [ "NICK", "Nickelodeon", "NICKAPP" ], "Norsk Rikskringkasting": "NRK", "OnDemandKorea": [ "ODK", "OnDemandKorea" ], "Opto": "OPTO", "Oprah Winfrey Network": "OWN", "Paramount+": [ "PMTP", "PMNP", "PMT+", "Paramount+", "ParamountPlus" ], "PBS": "PBS", "PBS Kids": "PBSK", "Peacock": [ "PCOK", "Peacock" ], "Playstation Network": "PSN", "Pluzz": "PLUZ", "PokerGO": "POGO", "Rakuten TV": "RKTN", "The Roku Channel": "ROKU", "RTE One": "RTE", "RUUTU": "RUUTU", "SBS": "SBS", "Science Channel": "SCI", "SeeSo": [ "SESO", "SeeSo" ], "Shomi": "SHMI", "Showtime": "SHO", "Sony": "SONY", "Spike": "SPIK", "Spike TV": [ "SPKE", "re:Spike-?TV" ], "Sportsnet": "SNET", "Sprout": "SPRT", "Stan": "STAN", "Starz": "STZ", "Sveriges Television": "SVT", "SwearNet": "SWER", "Syfy": "SYFY", "TBS": "TBS", "TFou": "TFOU", "The CW": [ "CW", "re:The-?CW" ], "TLC": "TLC", "TubiTV": "TUBI", "TV3 Ireland": "TV3", "TV4 Sweeden": "TV4", "TVING": "TVING", "TV Land": [ "TVL", "re:TV-?Land" ], "TVNZ": "TVNZ", "UFC": "UFC", "UFC Fight Pass": "FP", "UKTV": "UKTV", "Univision": "UNIV", "USA Network": "USAN", "Velocity": "VLCT", "VH1": "VH1", "Viceland": "VICE", "Viki": "VIKI", "Vimeo": "VMEO", "VRV": "VRV", "W Network": "WNET", "WatchMe": "WME", "WWE Network": "WWEN", "Xbox Video": "XBOX", "Yahoo": "YHOO", "YouTube Red": "RED", "ZDF": "ZDF" }, "date": { "week_words": ["week"] } } } guessit-3.8.0/guessit/data/000077500000000000000000000000001453655371700156215ustar00rootroot00000000000000guessit-3.8.0/guessit/data/__init__.py000066400000000000000000000000731453655371700177320ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Data """ guessit-3.8.0/guessit/data/tlds-alpha-by-domain.txt000066400000000000000000000032261453655371700222730ustar00rootroot00000000000000# Version 2013112900, Last Updated Fri Nov 29 07:07:01 2013 UTC AC AD AE AERO AF AG AI AL AM AN AO AQ AR ARPA AS ASIA AT AU AW AX AZ BA BB BD BE BF BG BH BI BIKE BIZ BJ BM BN BO BR BS BT BV BW BY BZ CA CAMERA CAT CC CD CF CG CH CI CK CL CLOTHING CM CN CO COM CONSTRUCTION CONTRACTORS COOP CR CU CV CW CX CY CZ DE DIAMONDS DIRECTORY DJ DK DM DO DZ EC EDU EE EG ENTERPRISES EQUIPMENT ER ES ESTATE ET EU FI FJ FK FM FO FR GA GALLERY GB GD GE GF GG GH GI GL GM GN GOV GP GQ GR GRAPHICS GS GT GU GURU GW GY HK HM HN HOLDINGS HR HT HU ID IE IL IM IN INFO INT IO IQ IR IS IT JE JM JO JOBS JP KE KG KH KI KITCHEN KM KN KP KR KW KY KZ LA LAND LB LC LI LIGHTING LK LR LS LT LU LV LY MA MC MD ME MG MH MIL MK ML MM MN MO MOBI MP MQ MR MS MT MU MUSEUM MV MW MX MY MZ NA NAME NC NE NET NF NG NI NL NO NP NR NU NZ OM ORG PA PE PF PG PH PHOTOGRAPHY PK PL PLUMBING PM PN POST PR PRO PS PT PW PY QA RE RO RS RU RW SA SB SC SD SE SEXY SG SH SI SINGLES SJ SK SL SM SN SO SR ST SU SV SX SY SZ TATTOO TC TD TECHNOLOGY TEL TF TG TH TIPS TJ TK TL TM TN TO TODAY TP TR TRAVEL TT TV TW TZ UA UG UK US UY UZ VA VC VE VENTURES VG VI VN VOYAGE VU WF WS XN--3E0B707E XN--45BRJ9C XN--80AO21A XN--80ASEHDB XN--80ASWG XN--90A3AC XN--CLCHC0EA0B2G2A9GCD XN--FIQS8S XN--FIQZ9S XN--FPCRJ9C3D XN--FZC2C9E2C XN--GECRJ9C XN--H2BRJ9C XN--J1AMH XN--J6W193G XN--KPRW13D XN--KPRY57D XN--L1ACC XN--LGBBAT1AD8J XN--MGB9AWBF XN--MGBA3A4F16A XN--MGBAAM7A8H XN--MGBAYH7GPA XN--MGBBH1A71E XN--MGBC0A9AZCG XN--MGBERP4A5D4AR XN--MGBX4CD0AB XN--NGBC5AZD XN--O3CW4H XN--OGBPF8FL XN--P1AI XN--PGBS0DH XN--Q9JYB4C XN--S9BRJ9C XN--UNUP4Y XN--WGBH1C XN--WGBL6A XN--XKC2AL3HYE2A XN--XKC2DL3A5EE0H XN--YFRO4I67O XN--YGBI2AMMX XXX YE YT ZA ZM ZW guessit-3.8.0/guessit/jsonutils.py000066400000000000000000000010061453655371700173110ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ JSON Utils """ import json from six import text_type from rebulk.match import Match class GuessitEncoder(json.JSONEncoder): """ JSON Encoder for guessit response """ def default(self, o): # pylint:disable=method-hidden if isinstance(o, Match): return o.advanced if hasattr(o, 'name'): # Babelfish languages/countries long name return text_type(o.name) # pragma: no cover return text_type(o) guessit-3.8.0/guessit/monkeypatch.py000066400000000000000000000011411453655371700176010ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Monkeypatch initialisation functions """ from collections import OrderedDict from rebulk.match import Match def monkeypatch_rebulk(): """Monkeypatch rebulk classes""" @property def match_advanced(self): """ Build advanced dict from match :param self: :return: """ ret = OrderedDict() ret['value'] = self.value if self.raw: ret['raw'] = self.raw ret['start'] = self.start ret['end'] = self.end return ret Match.advanced = match_advanced guessit-3.8.0/guessit/options.py000066400000000000000000000315601453655371700167620ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Options """ import copy import json import os import shlex import sys from argparse import ArgumentParser # importlib.resources.read_text() is deprecated since Python 3.11. # importlib.resources.files() is new in Python 3.9. if sys.version_info >= (3, 9, 0): from importlib.resources import files def read_text(package, filename): """ Should behave like deprecated importlib.resources.read_text() """ return files(package).joinpath(filename).read_text() # pylint:disable=unspecified-encoding else: try: from importlib.resources import read_text except ImportError: from importlib_resources import read_text def build_argument_parser(): """ Builds the argument parser :return: the argument parser :rtype: ArgumentParser """ opts = ArgumentParser() opts.add_argument(dest='filename', help='Filename or release name to guess', nargs='*') naming_opts = opts.add_argument_group("Naming") naming_opts.add_argument('-t', '--type', dest='type', default=None, help='The suggested file type: movie, episode. If undefined, type will be guessed.') naming_opts.add_argument('-n', '--name-only', dest='name_only', action='store_true', default=None, help='Parse files as name only, considering "/" and "\\" like other separators.') naming_opts.add_argument('-Y', '--date-year-first', action='store_true', dest='date_year_first', default=None, help='If short date is found, consider the first digits as the year.') naming_opts.add_argument('-D', '--date-day-first', action='store_true', dest='date_day_first', default=None, help='If short date is found, consider the second digits as the day.') naming_opts.add_argument('-L', '--allowed-languages', action='append', dest='allowed_languages', default=None, help='Allowed language (can be used multiple times)') naming_opts.add_argument('-C', '--allowed-countries', action='append', dest='allowed_countries', default=None, help='Allowed country (can be used multiple times)') naming_opts.add_argument('-E', '--episode-prefer-number', action='store_true', dest='episode_prefer_number', default=None, help='Guess "serie.213.avi" as the episode 213. Without this option, ' 'it will be guessed as season 2, episode 13') naming_opts.add_argument('-T', '--expected-title', action='append', dest='expected_title', default=None, help='Expected title to parse (can be used multiple times)') naming_opts.add_argument('-G', '--expected-group', action='append', dest='expected_group', default=None, help='Expected release group (can be used multiple times)') naming_opts.add_argument('--includes', action='append', dest='includes', default=None, help='List of properties to be detected') naming_opts.add_argument('--excludes', action='append', dest='excludes', default=None, help='List of properties to be ignored') input_opts = opts.add_argument_group("Input") input_opts.add_argument('-f', '--input-file', dest='input_file', default=None, help='Read filenames from an input text file. File should use UTF-8 charset.') output_opts = opts.add_argument_group("Output") output_opts.add_argument('-v', '--verbose', action='store_true', dest='verbose', default=None, help='Display debug output') output_opts.add_argument('-P', '--show-property', dest='show_property', default=None, help='Display the value of a single property (title, series, video_codec, year, ...)') output_opts.add_argument('-a', '--advanced', dest='advanced', action='store_true', default=None, help='Display advanced information for filename guesses, as json output') output_opts.add_argument('-s', '--single-value', dest='single_value', action='store_true', default=None, help='Keep only first value found for each property') output_opts.add_argument('-l', '--enforce-list', dest='enforce_list', action='store_true', default=None, help='Wrap each found value in a list even when property has a single value') output_opts.add_argument('-j', '--json', dest='json', action='store_true', default=None, help='Display information for filename guesses as json output') output_opts.add_argument('-y', '--yaml', dest='yaml', action='store_true', default=None, help='Display information for filename guesses as yaml output') output_opts.add_argument('-i', '--output-input-string', dest='output_input_string', action='store_true', default=False, help='Add input_string property in the output') conf_opts = opts.add_argument_group("Configuration") conf_opts.add_argument('-c', '--config', dest='config', action='append', default=None, help='Filepath to configuration file. Configuration file contains the same ' 'options as those from command line options, but option names have "-" characters ' 'replaced with "_". This configuration will be merged with default and user ' 'configuration files.') conf_opts.add_argument('--no-user-config', dest='no_user_config', action='store_true', default=None, help='Disable user configuration. If not defined, guessit tries to read configuration files ' 'at ~/.guessit/options.(json|yml|yaml) and ~/.config/guessit/options.(json|yml|yaml)') conf_opts.add_argument('--no-default-config', dest='no_default_config', action='store_true', default=None, help='Disable default configuration. This should be done only if you are providing a full ' 'configuration through user configuration or --config option. If no "advanced_config" ' 'is provided by another configuration file, it will still be loaded from default ' 'configuration.') information_opts = opts.add_argument_group("Information") information_opts.add_argument('-p', '--properties', dest='properties', action='store_true', default=None, help='Display properties that can be guessed.') information_opts.add_argument('-V', '--values', dest='values', action='store_true', default=None, help='Display property values that can be guessed.') information_opts.add_argument('--version', dest='version', action='store_true', default=None, help='Display the guessit version.') return opts def parse_options(options=None, api=False): """ Parse given option string :param options: :type options: :param api :type api: boolean :return: :rtype: """ if isinstance(options, str): args = shlex.split(options) options = vars(argument_parser.parse_args(args)) elif options is None: if api: options = {} else: options = vars(argument_parser.parse_args()) elif not isinstance(options, dict): options = vars(argument_parser.parse_args(options)) return options argument_parser = build_argument_parser() class ConfigurationException(Exception): """ Exception related to configuration file. """ pass # pylint:disable=unnecessary-pass def load_config(options): """ Load options from configuration files, if defined and present. :param options: :type options: :return: :rtype: """ configurations = [] if not options.get('no_default_config'): default_options_data = read_text('guessit.config', 'options.json') default_options = json.loads(default_options_data) configurations.append(default_options) config_files = [] if not options.get('no_user_config'): home_directory = os.path.expanduser("~") cwd = os.getcwd() yaml_supported = False try: import yaml # pylint:disable=unused-variable,unused-import,import-outside-toplevel yaml_supported = True except ImportError: pass config_file_locations = get_options_file_locations(home_directory, cwd, yaml_supported) config_files = [f for f in config_file_locations if os.path.exists(f)] custom_config_files = options.get('config') if custom_config_files: config_files = config_files + custom_config_files for config_file in config_files: config_file_options = load_config_file(config_file) if config_file_options: configurations.append(config_file_options) config = {} if configurations: config = merge_options(*configurations) if 'advanced_config' not in config: # Guessit doesn't work without advanced_config, so we use default if no configuration files provides it. default_options_data = read_text('guessit.config', 'options.json') default_options = json.loads(default_options_data) config['advanced_config'] = default_options['advanced_config'] return config def merge_options(*options): """ Merge options into a single options dict. :param options: :type options: :return: :rtype: """ merged = {} if options: if options[0]: merged.update(copy.deepcopy(options[0])) for options in options[1:]: if options: pristine = options.get('pristine') if pristine is True: merged = {} elif pristine: for to_reset in pristine: if to_reset in merged: del merged[to_reset] for (option, value) in options.items(): merge_option_value(option, value, merged) return merged def merge_option_value(option, value, merged): """ Merge option value :param option: :param value: :param merged: :return: """ if value is not None and option != 'pristine': if option in merged.keys() and isinstance(merged[option], list): for val in value: if val not in merged[option] and val is not None: merged[option].append(val) elif option in merged.keys() and isinstance(merged[option], dict): merged[option] = merge_options(merged[option], value) elif isinstance(value, list): merged[option] = list(value) else: merged[option] = value def load_config_file(filepath): """ Load a configuration as an options dict. Format of the file is given with filepath extension. :param filepath: :type filepath: :return: :rtype: """ if filepath.endswith('.json'): with open(filepath, encoding='utf-8') as config_file_data: return json.load(config_file_data) if filepath.endswith('.yaml') or filepath.endswith('.yml'): try: import yaml # pylint:disable=import-outside-toplevel with open(filepath, encoding='utf-8') as config_file_data: return yaml.load(config_file_data, yaml.SafeLoader) except ImportError as err: # pragma: no cover raise ConfigurationException('Configuration file extension is not supported. ' f'PyYAML should be installed to support "{filepath}" file') from err try: # Try to load input as JSON return json.loads(filepath) except: # pylint: disable=bare-except pass raise ConfigurationException(f'Configuration file extension is not supported for "{filepath}" file.') def get_options_file_locations(homedir, cwd, yaml_supported=False): """ Get all possible locations for options file. :param homedir: user home directory :type homedir: basestring :param cwd: current working directory :type homedir: basestring :return: :rtype: list """ locations = [] configdirs = [(os.path.join(homedir, '.guessit'), 'options'), (os.path.join(homedir, '.config', 'guessit'), 'options'), (cwd, 'guessit.options')] configexts = ['json'] if yaml_supported: configexts.append('yaml') configexts.append('yml') for configdir in configdirs: for configext in configexts: locations.append(os.path.join(configdir[0], configdir[1] + '.' + configext)) return locations guessit-3.8.0/guessit/reutils.py000066400000000000000000000014441453655371700167540ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Utils for re module """ from rebulk.remodule import re def build_or_pattern(patterns, name=None, escape=False): """ Build a or pattern string from a list of possible patterns :param patterns: :type patterns: :param name: :type name: :param escape: :type escape: :return: :rtype: """ or_pattern = [] for pattern in patterns: if not or_pattern: or_pattern.append('(?') if name: or_pattern.append(f'P<{name}>') else: or_pattern.append(':') else: or_pattern.append('|') or_pattern.append(f'(?:{re.escape(pattern)})' if escape else pattern) or_pattern.append(')') return ''.join(or_pattern) guessit-3.8.0/guessit/rules/000077500000000000000000000000001453655371700160425ustar00rootroot00000000000000guessit-3.8.0/guessit/rules/__init__.py000066400000000000000000000062711453655371700201610ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Rebulk object default builder """ from rebulk import Rebulk from .markers.path import path from .markers.groups import groups from .properties.episodes import episodes from .properties.container import container from .properties.source import source from .properties.video_codec import video_codec from .properties.audio_codec import audio_codec from .properties.screen_size import screen_size from .properties.website import website from .properties.date import date from .properties.title import title from .properties.episode_title import episode_title from .properties.language import language from .properties.country import country from .properties.release_group import release_group 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.cd import cd from .properties.bonus import bonus from .properties.film import film from .properties.part import part from .properties.crc import crc from .properties.mimetype import mimetype from .properties.type import type_ from .processors import processors def rebulk_builder(config): """ Default builder for main Rebulk object used by api. :return: Main Rebulk object :rtype: Rebulk """ def _config(name): return config.get(name, {}) rebulk = Rebulk() common_words = frozenset(_config('common_words')) rebulk.rebulk(path(_config('path'))) rebulk.rebulk(groups(_config('groups'))) rebulk.rebulk(episodes(_config('episodes'))) rebulk.rebulk(container(_config('container'))) rebulk.rebulk(source(_config('source'))) rebulk.rebulk(video_codec(_config('video_codec'))) rebulk.rebulk(audio_codec(_config('audio_codec'))) rebulk.rebulk(screen_size(_config('screen_size'))) rebulk.rebulk(website(_config('website'))) rebulk.rebulk(date(_config('date'))) rebulk.rebulk(title(_config('title'))) rebulk.rebulk(episode_title(_config('episode_title'))) rebulk.rebulk(language(_config('language'), common_words)) rebulk.rebulk(country(_config('country'), common_words)) rebulk.rebulk(release_group(_config('release_group'))) rebulk.rebulk(streaming_service(_config('streaming_service'))) rebulk.rebulk(other(_config('other'))) rebulk.rebulk(size(_config('size'))) rebulk.rebulk(bit_rate(_config('bit_rate'))) rebulk.rebulk(edition(_config('edition'))) rebulk.rebulk(cd(_config('cd'))) rebulk.rebulk(bonus(_config('bonus'))) rebulk.rebulk(film(_config('film'))) rebulk.rebulk(part(_config('part'))) rebulk.rebulk(crc(_config('crc'))) rebulk.rebulk(processors(_config('processors'))) rebulk.rebulk(mimetype(_config('mimetype'))) rebulk.rebulk(type_(_config('type'))) def customize_properties(properties): """ Customize default rebulk properties """ count = properties['count'] del properties['count'] properties['season_count'] = count properties['episode_count'] = count return properties rebulk.customize_properties = customize_properties return rebulk guessit-3.8.0/guessit/rules/common/000077500000000000000000000000001453655371700173325ustar00rootroot00000000000000guessit-3.8.0/guessit/rules/common/__init__.py000066400000000000000000000011361453655371700214440ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Common module """ from rebulk.remodule import re seps = r' [](){}+*|=-_~#/\\.,;:' # list of tags/words separators seps_no_groups = seps.replace('[](){}', '') seps_no_fs = seps.replace('/', '').replace('\\', '') title_seps = r'-+/\|' # separators for title dash = (r'-', r'['+re.escape(seps_no_fs)+']') # abbreviation used by many rebulk objects. alt_dash = (r'@', r'['+re.escape(seps_no_fs)+']') # abbreviation used by many rebulk objects. def optional(pattern): """ Make a regex pattern optional """ return '(?:' + pattern + ')?' guessit-3.8.0/guessit/rules/common/comparators.py000066400000000000000000000035541453655371700222450ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Comparators """ from functools import cmp_to_key def marker_comparator_predicate(match): """ Match predicate used in comparator """ return ( not match.private and match.name not in ('proper_count', 'title') and not (match.name == 'container' and 'extension' in match.tags) and not (match.name == 'other' and match.value == 'Rip') ) def marker_weight(matches, marker, predicate): """ Compute the comparator weight of a marker :param matches: :param marker: :param predicate: :return: """ return len(set(match.name for match in matches.range(*marker.span, predicate=predicate))) def marker_comparator(matches, markers, predicate): """ Builds a comparator that returns markers sorted from the most valuable to the less. Take the parts where matches count is higher, then when length is higher, then when position is at left. :param matches: :type matches: :param markers: :param predicate: :return: :rtype: """ def comparator(marker1, marker2): """ The actual comparator function. """ matches_count = marker_weight(matches, marker2, predicate) - marker_weight(matches, marker1, predicate) if matches_count: return matches_count # give preference to rightmost path return markers.index(marker2) - markers.index(marker1) return comparator def marker_sorted(markers, matches, predicate=marker_comparator_predicate): """ Sort markers from matches, from the most valuable to the less. :param markers: :type markers: :param matches: :type matches: :param predicate: :return: :rtype: """ return sorted(markers, key=cmp_to_key(marker_comparator(matches, markers, predicate=predicate))) guessit-3.8.0/guessit/rules/common/date.py000066400000000000000000000112101453655371700206140ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Date """ from dateutil import parser from rebulk.remodule import re _dsep = r'[-/ \.]' _dsep_bis = r'[-/ \.x]' date_regexps = [ # pylint:disable=consider-using-f-string re.compile(r'%s((\d{8}))%s' % (_dsep, _dsep), re.IGNORECASE), # pylint:disable=consider-using-f-string re.compile(r'%s((\d{6}))%s' % (_dsep, _dsep), re.IGNORECASE), # pylint:disable=consider-using-f-string re.compile(r'(?:^|[^\d])((\d{2})%s(\d{1,2})%s(\d{1,2}))(?:$|[^\d])' % (_dsep, _dsep), re.IGNORECASE), # pylint:disable=consider-using-f-string re.compile(r'(?:^|[^\d])((\d{1,2})%s(\d{1,2})%s(\d{2}))(?:$|[^\d])' % (_dsep, _dsep), re.IGNORECASE), # pylint:disable=consider-using-f-string re.compile(r'(?:^|[^\d])((\d{4})%s(\d{1,2})%s(\d{1,2}))(?:$|[^\d])' % (_dsep_bis, _dsep), re.IGNORECASE), # pylint:disable=consider-using-f-string re.compile(r'(?:^|[^\d])((\d{1,2})%s(\d{1,2})%s(\d{4}))(?:$|[^\d])' % (_dsep, _dsep_bis), re.IGNORECASE), # pylint:disable=consider-using-f-string re.compile(r'(?:^|[^\d])((\d{1,2}(?:st|nd|rd|th)?%s(?:[a-z]{3,10})%s\d{4}))(?:$|[^\d])' % (_dsep, _dsep), # pylint:disable=consider-using-f-string re.IGNORECASE)] def valid_year(year): """Check if number is a valid year""" return 1920 <= year < 2030 def valid_week(week): """Check if number is a valid week""" return 1 <= week < 53 def _is_int(string): """ Check if the input string is an integer :param string: :type string: :return: :rtype: """ try: int(string) return True except ValueError: return False def _guess_day_first_parameter(groups): # pylint:disable=inconsistent-return-statements """ If day_first is not defined, use some heuristic to fix it. It helps to solve issues with python dateutils 2.5.3 parser changes. :param groups: match groups found for the date :type groups: list of match objects :return: day_first option guessed value :rtype: bool """ # If match starts with a long year, then day_first is force to false. if _is_int(groups[0]) and valid_year(int(groups[0][:4])): return False # If match ends with a long year, the day_first is forced to true. if _is_int(groups[-1]) and valid_year(int(groups[-1][-4:])): return True # If match starts with a short year, then day_first is force to false. if _is_int(groups[0]) and int(groups[0][:2]) > 31: return False # If match ends with a short year, then day_first is force to true. if _is_int(groups[-1]) and int(groups[-1][-2:]) > 31: return True def search_date(string, year_first=None, day_first=None): # pylint:disable=inconsistent-return-statements """Looks for date patterns, and if found return the date and group span. Assumes there are sentinels at the beginning and end of the string that always allow matching a non-digit delimiting the date. Year can be defined on two digit only. It will return the nearest possible date from today. >>> search_date(' This happened on 2002-04-22. ') (18, 28, datetime.date(2002, 4, 22)) >>> search_date(' And this on 17-06-1998. ') (13, 23, datetime.date(1998, 6, 17)) >>> search_date(' no date in here ') """ for date_re in date_regexps: search_match = date_re.search(string) if not search_match: continue start, end = search_match.start(1), search_match.end(1) groups = search_match.groups()[1:] match = '-'.join(groups) if match is None: continue if year_first and day_first is None: day_first = False if day_first is None: day_first = _guess_day_first_parameter(groups) # If day_first/year_first is undefined, parse is made using both possible values. yearfirst_opts = [False, True] if year_first is not None: yearfirst_opts = [year_first] dayfirst_opts = [True, False] if day_first is not None: dayfirst_opts = [day_first] kwargs_list = ({'dayfirst': d, 'yearfirst': y} for d in dayfirst_opts for y in yearfirst_opts) for kwargs in kwargs_list: try: date = parser.parse(match, **kwargs) except (ValueError, TypeError): # pragma: no cover # see https://bugs.launchpad.net/dateutil/+bug/1247643 date = None # check date plausibility if date and valid_year(date.year): # pylint:disable=no-member return start, end, date.date() # pylint:disable=no-member guessit-3.8.0/guessit/rules/common/expected.py000066400000000000000000000030061453655371700215040ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Expected property factory """ from rebulk import Rebulk from rebulk.remodule import re from rebulk.utils import find_all from . import dash, seps def build_expected_function(context_key): """ Creates a expected property function :param context_key: :type context_key: :param cleanup: :type cleanup: :return: :rtype: """ def expected(input_string, context): """ Expected property functional pattern. :param input_string: :type input_string: :param context: :type context: :return: :rtype: """ ret = [] for search in context.get(context_key): if search.startswith('re:'): search = search[3:] search = search.replace(' ', '-') matches = Rebulk().regex(search, abbreviations=[dash], flags=re.IGNORECASE) \ .matches(input_string, context) for match in matches: ret.append(match.span) else: for sep in seps: input_string = input_string.replace(sep, ' ') search = search.replace(sep, ' ') for start in find_all(input_string, search, ignore_case=True): end = start + len(search) value = input_string[start:end] ret.append({'start': start, 'end': end, 'value': value}) return ret return expected guessit-3.8.0/guessit/rules/common/formatters.py000066400000000000000000000070651453655371700221020ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Formatters """ from rebulk.formatters import formatters from rebulk.remodule import re from . import seps _excluded_clean_chars = ',:;-/\\' clean_chars = "" for sep in seps: if sep not in _excluded_clean_chars: clean_chars += sep def _potential_before(i, input_string): """ Check if the character at position i can be a potential single char separator considering what's before it. :param i: :type i: int :param input_string: :type input_string: str :return: :rtype: bool """ return i - 1 >= 0 and input_string[i] in seps and input_string[i - 2] in seps and input_string[i - 1] not in seps def _potential_after(i, input_string): """ Check if the character at position i can be a potential single char separator considering what's after it. :param i: :type i: int :param input_string: :type input_string: str :return: :rtype: bool """ return i + 2 >= len(input_string) or \ input_string[i + 2] == input_string[i] and input_string[i + 1] not in seps def cleanup(input_string): """ Removes and strip separators from input_string (but keep ',;' characters) It also keep separators for single characters (Mavels Agents of S.H.I.E.L.D.) :param input_string: :type input_string: str :return: :rtype: """ clean_string = input_string for char in clean_chars: clean_string = clean_string.replace(char, ' ') # Restore input separator if they separate single characters. # Useful for Mavels Agents of S.H.I.E.L.D. # https://github.com/guessit-io/guessit/issues/278 indices = [i for i, letter in enumerate(clean_string) if letter in seps] dots = set() if indices: clean_list = list(clean_string) potential_indices = [] for i in indices: if _potential_before(i, input_string) and _potential_after(i, input_string): potential_indices.append(i) replace_indices = [] for potential_index in potential_indices: if potential_index - 2 in potential_indices or potential_index + 2 in potential_indices: replace_indices.append(potential_index) if replace_indices: for replace_index in replace_indices: dots.add(input_string[replace_index]) clean_list[replace_index] = input_string[replace_index] clean_string = ''.join(clean_list) clean_string = strip(clean_string, ''.join([c for c in seps if c not in dots])) clean_string = re.sub(' +', ' ', clean_string) return clean_string def strip(input_string, chars=seps): """ Strip separators from input_string :param input_string: :param chars: :type input_string: :return: :rtype: """ return input_string.strip(chars) def raw_cleanup(raw): """ Cleanup a raw value to perform raw comparison :param raw: :type raw: :return: :rtype: """ return formatters(cleanup, strip)(raw.lower()) def reorder_title(title, articles=('the',), separators=(',', ', ')): """ Reorder the title :param title: :type title: :param articles: :type articles: :param separators: :type separators: :return: :rtype: """ ltitle = title.lower() for article in articles: for separator in separators: suffix = separator + article if ltitle[-len(suffix):] == suffix: return title[-len(suffix) + len(separator):] + ' ' + title[:-len(suffix)] return title guessit-3.8.0/guessit/rules/common/numeral.py000066400000000000000000000105271453655371700213540ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ parse numeral from various formats """ from rebulk.remodule import re digital_numeral = r'\d{1,4}' roman_numeral = r'(?=[MCDLXVI]+)M{0,4}(?:CM|CD|D?C{0,3})(?:XC|XL|L?X{0,3})(?:IX|IV|V?I{0,3})' english_word_numeral_list = [ 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty' ] french_word_numeral_list = [ 'zéro', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf', 'dix', 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf', 'vingt' ] french_alt_word_numeral_list = [ 'zero', 'une', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf', 'dix', 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dixsept', 'dixhuit', 'dixneuf', 'vingt' ] def __build_word_numeral(*args): """ Build word numeral regexp from list. :param args: :type args: :param kwargs: :type kwargs: :return: :rtype: """ re_ = None for word_list in args: for word in word_list: if not re_: re_ = r'(?:(?=\w+)' else: re_ += '|' re_ += word re_ += ')' return re_ word_numeral = __build_word_numeral(english_word_numeral_list, french_word_numeral_list, french_alt_word_numeral_list) numeral = '(?:' + digital_numeral + '|' + roman_numeral + '|' + word_numeral + ')' __romanNumeralMap = ( ('M', 1000), ('CM', 900), ('D', 500), ('CD', 400), ('C', 100), ('XC', 90), ('L', 50), ('XL', 40), ('X', 10), ('IX', 9), ('V', 5), ('IV', 4), ('I', 1) ) __romanNumeralPattern = re.compile('^' + roman_numeral + '$') def __parse_roman(value): """ convert Roman numeral to integer :param value: Value to parse :type value: string :return: :rtype: """ if not __romanNumeralPattern.search(value): raise ValueError(f'Invalid Roman numeral: {value}') result = 0 index = 0 for num, integer in __romanNumeralMap: while value[index:index + len(num)] == num: result += integer index += len(num) return result def __parse_word(value): """ Convert Word numeral to integer :param value: Value to parse :type value: string :return: :rtype: """ for word_list in [english_word_numeral_list, french_word_numeral_list, french_alt_word_numeral_list]: try: return word_list.index(value.lower()) except ValueError: pass raise ValueError # pragma: no cover _clean_re = re.compile(r'[^\d]*(\d+)[^\d]*') def parse_numeral(value, int_enabled=True, roman_enabled=True, word_enabled=True, clean=True): """ Parse a numeric value into integer. :param value: Value to parse. Can be an integer, roman numeral or word. :type value: string :param int_enabled: :type int_enabled: :param roman_enabled: :type roman_enabled: :param word_enabled: :type word_enabled: :param clean: :type clean: :return: Numeric value, or None if value can't be parsed :rtype: int """ # pylint: disable=too-many-branches if int_enabled: try: if clean: match = _clean_re.match(value) if match: clean_value = match.group(1) return int(clean_value) return int(value) except ValueError: pass if roman_enabled: try: if clean: for word in value.split(): try: return __parse_roman(word.upper()) except ValueError: pass return __parse_roman(value) except ValueError: pass if word_enabled: try: if clean: for word in value.split(): try: return __parse_word(word) except ValueError: # pragma: no cover pass return __parse_word(value) # pragma: no cover except ValueError: # pragma: no cover pass raise ValueError('Invalid numeral: ' + value) # pragma: no cover guessit-3.8.0/guessit/rules/common/pattern.py000066400000000000000000000013071453655371700213620ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Pattern utility functions """ def is_disabled(context, name): """Whether a specific pattern is disabled. The context object might define an inclusion list (includes) or an exclusion list (excludes) A pattern is considered disabled if it's found in the exclusion list or it's not found in the inclusion list and the inclusion list is not empty or not defined. :param context: :param name: :return: """ if not context: return False excludes = context.get('excludes') if excludes and name in excludes: return True includes = context.get('includes') return includes and name not in includes guessit-3.8.0/guessit/rules/common/quantity.py000066400000000000000000000043051453655371700215640ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Quantities: Size """ from abc import abstractmethod from rebulk.remodule import re from ..common import seps class Quantity: """ Represent a quantity object with magnitude and units. """ parser_re = re.compile(r'(?P\d+(?:[.]\d+)?)(?P[^\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, str): 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 f'<{self.__class__.__name__} [{self}]>' def __str__(self): return f'{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): value = value.strip(seps).capitalize() for token in ('bits', 'bit'): value = value.replace(token, 'bps') return value class FrameRate(Quantity): """ Represent frame rate. e.g.: 24fps, 60fps """ @classmethod def parse_units(cls, value): return 'fps' guessit-3.8.0/guessit/rules/common/validators.py000066400000000000000000000026631453655371700220630ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Validators """ from functools import partial from rebulk.validators import chars_before, chars_after, chars_surround from . import seps seps_before = partial(chars_before, seps) seps_after = partial(chars_after, seps) seps_surround = partial(chars_surround, seps) def int_coercable(string): """ Check if string can be coerced to int :param string: :type string: :return: :rtype: """ try: int(string) return True except ValueError: return False def and_(*validators): """ Compose validators functions :param validators: :type validators: :return: :rtype: """ def composed(string): """ Composed validators function :param string: :type string: :return: :rtype: """ for validator in validators: if not validator(string): return False return True return composed def or_(*validators): """ Compose validators functions :param validators: :type validators: :return: :rtype: """ def composed(string): """ Composed validators function :param string: :type string: :return: :rtype: """ for validator in validators: if validator(string): return True return False return composed guessit-3.8.0/guessit/rules/common/words.py000066400000000000000000000015141453655371700210430ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Words utils """ from collections import namedtuple from . import seps _Word = namedtuple('_Word', ['span', 'value']) def iter_words(string): """ Iterate on all words in a string :param string: :type string: :return: :rtype: iterable[str] """ i = 0 last_sep_index = -1 inside_word = False for char in string: if ord(char) < 128 and char in seps: # Make sure we don't exclude unicode characters. if inside_word: yield _Word(span=(last_sep_index+1, i), value=string[last_sep_index+1:i]) inside_word = False last_sep_index = i else: inside_word = True i += 1 if inside_word: yield _Word(span=(last_sep_index+1, i), value=string[last_sep_index+1:i]) guessit-3.8.0/guessit/rules/markers/000077500000000000000000000000001453655371700175065ustar00rootroot00000000000000guessit-3.8.0/guessit/rules/markers/__init__.py000066400000000000000000000000761453655371700216220ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Markers """ guessit-3.8.0/guessit/rules/markers/groups.py000066400000000000000000000025351453655371700214040ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Groups markers (...), [...] and {...} """ from rebulk import Rebulk from ...options import ConfigurationException def groups(config): """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk() rebulk.defaults(name="group", marker=True) starting = config['starting'] ending = config['ending'] if len(starting) != len(ending): raise ConfigurationException("Starting and ending groups must have the same length") def mark_groups(input_string): """ Functional pattern to mark groups (...), [...] and {...}. :param input_string: :return: """ openings = ([], ) * len(starting) i = 0 ret = [] for char in input_string: start_type = starting.find(char) if start_type > -1: openings[start_type].append(i) i += 1 end_type = ending.find(char) if end_type > -1: try: start_index = openings[end_type].pop() ret.append((start_index, i)) except IndexError: pass return ret rebulk.functional(mark_groups) return rebulk guessit-3.8.0/guessit/rules/markers/path.py000066400000000000000000000021071453655371700210140ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Path markers """ from rebulk import Rebulk from rebulk.utils import find_all def path(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk() rebulk.defaults(name="path", marker=True) def mark_path(input_string, context): """ Functional pattern to mark path elements. :param input_string: :param context: :return: """ ret = [] if context.get('name_only', False): ret.append((0, len(input_string))) else: indices = list(find_all(input_string, '/')) indices += list(find_all(input_string, '\\')) indices += [-1, len(input_string)] indices.sort() for i in range(0, len(indices) - 1): ret.append((indices[i] + 1, indices[i + 1])) return ret rebulk.functional(mark_path) return rebulk guessit-3.8.0/guessit/rules/match_processors.py000066400000000000000000000006311453655371700217720ustar00rootroot00000000000000""" Match processors """ from guessit.rules.common import seps def strip(match, chars=seps): """ Strip given characters from match. :param chars: :param match: :return: """ while match.input_string[match.start] in chars: match.start += 1 while match.input_string[match.end - 1] in chars: match.end -= 1 if not match: return False return None guessit-3.8.0/guessit/rules/processors.py000066400000000000000000000201611453655371700206160ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Processors """ from collections import defaultdict import copy from rebulk import Rebulk, Rule, CustomRule, POST_PROCESS, PRE_PROCESS, AppendMatch, RemoveMatch from .common import seps_no_groups from .common.formatters import cleanup from .common.comparators import marker_sorted from .common.date import valid_year from .common.words import iter_words class EnlargeGroupMatches(CustomRule): """ Enlarge matches that are starting and/or ending group to include brackets in their span. """ priority = PRE_PROCESS def when(self, matches, context): starting = [] ending = [] for group in matches.markers.named('group'): for match in matches.starting(group.start + 1): starting.append(match) for match in matches.ending(group.end - 1): ending.append(match) if starting or ending: return starting, ending return False def then(self, matches, when_response, context): starting, ending = when_response for match in starting: matches.remove(match) match.start -= 1 match.raw_start += 1 matches.append(match) for match in ending: matches.remove(match) match.end += 1 match.raw_end -= 1 matches.append(match) class EquivalentHoles(Rule): """ Creates equivalent matches for holes that have same values than existing (case insensitive) """ priority = POST_PROCESS consequence = AppendMatch def when(self, matches, context): new_matches = [] for filepath in marker_sorted(matches.markers.named('path'), matches): holes = matches.holes(start=filepath.start, end=filepath.end, formatter=cleanup) for name in matches.names: for hole in list(holes): for current_match in matches.named(name): if isinstance(current_match.value, str) and \ hole.value.lower() == current_match.value.lower(): if 'equivalent-ignore' in current_match.tags: continue new_value = _preferred_string(hole.value, current_match.value) if hole.value != new_value: hole.value = new_value if current_match.value != new_value: current_match.value = new_value hole.name = name hole.tags = ['equivalent'] new_matches.append(hole) if hole in holes: holes.remove(hole) return new_matches class RemoveAmbiguous(Rule): """ If multiple matches are found with same name and different values, keep the one in the most valuable filepart. Also keep others match with same name and values than those kept ones. """ priority = POST_PROCESS consequence = RemoveMatch def __init__(self, sort_function=marker_sorted, predicate=None): super().__init__() self.sort_function = sort_function self.predicate = predicate def when(self, matches, context): fileparts = self.sort_function(matches.markers.named('path'), matches) previous_fileparts_names = set() values = defaultdict(list) to_remove = [] for filepart in fileparts: filepart_matches = matches.range(filepart.start, filepart.end, predicate=self.predicate) filepart_names = set() for match in filepart_matches: filepart_names.add(match.name) if match.name in previous_fileparts_names: if match.value not in values[match.name]: to_remove.append(match) else: if match.value not in values[match.name]: values[match.name].append(match.value) previous_fileparts_names.update(filepart_names) return to_remove class RemoveLessSpecificSeasonEpisode(RemoveAmbiguous): """ If multiple season/episodes matches are found with different values, keep the one tagged as 'SxxExx' or in the rightmost filepart. """ def __init__(self, name): super().__init__( sort_function=(lambda markers, matches: marker_sorted(list(reversed(markers)), matches, lambda match: match.name == name and 'SxxExx' in match.tags)), predicate=lambda match: match.name == name) def _preferred_string(value1, value2): # pylint:disable=too-many-return-statements """ Retrieves preferred title from both values. :param value1: :type value1: str :param value2: :type value2: str :return: The preferred title :rtype: str """ if value1 == value2: return value1 if value1.istitle() and not value2.istitle(): return value1 if not value1.isupper() and value2.isupper(): return value1 if not value1.isupper() and value1[0].isupper() and not value2[0].isupper(): return value1 if _count_title_words(value1) > _count_title_words(value2): return value1 return value2 def _count_title_words(value): """ Count only many words are titles in value. :param value: :type value: :return: :rtype: """ ret = 0 for word in iter_words(value): if word.value.istitle(): ret += 1 return ret class SeasonYear(Rule): """ If a season is a valid year and no year was found, create an match with year. """ priority = POST_PROCESS consequence = AppendMatch def when(self, matches, context): ret = [] if not matches.named('year'): for season in matches.named('season'): if valid_year(season.value): year = copy.copy(season) year.name = 'year' ret.append(year) return ret class YearSeason(Rule): """ If a year is found, no season found, and episode is found, create an match with season. """ priority = POST_PROCESS consequence = AppendMatch def when(self, matches, context): ret = [] if not matches.named('season') and matches.named('episode'): for year in matches.named('year'): season = copy.copy(year) season.name = 'season' ret.append(season) return ret class Processors(CustomRule): """ Empty rule for ordering post_processing properly. """ priority = POST_PROCESS def when(self, matches, context): pass def then(self, matches, when_response, context): # pragma: no cover pass class StripSeparators(CustomRule): """ Strip separators from matches. Keep separators if they are from acronyms, like in ".S.H.I.E.L.D." """ priority = POST_PROCESS def when(self, matches, context): return matches def then(self, matches, when_response, context): # pragma: no cover for match in matches: for _ in range(0, len(match.span)): if match.raw[0] in seps_no_groups and (len(match.raw) < 3 or match.raw[2] not in seps_no_groups): match.raw_start += 1 for _ in reversed(range(0, len(match.span))): if match.raw[-1] in seps_no_groups and (len(match.raw) < 3 or match.raw[-3] not in seps_no_groups): match.raw_end -= 1 def processors(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ return Rebulk().rules(EnlargeGroupMatches, EquivalentHoles, RemoveLessSpecificSeasonEpisode('season'), RemoveLessSpecificSeasonEpisode('episode'), RemoveAmbiguous, SeasonYear, YearSeason, Processors, StripSeparators) guessit-3.8.0/guessit/rules/properties/000077500000000000000000000000001453655371700202365ustar00rootroot00000000000000guessit-3.8.0/guessit/rules/properties/__init__.py000066400000000000000000000001011453655371700223370ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Properties """ guessit-3.8.0/guessit/rules/properties/audio_codec.py000066400000000000000000000145071453655371700230550ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ audio_codec, audio_profile and audio_channels property """ from rebulk import Rebulk, Rule, RemoveMatch from rebulk.remodule import re from ..common import dash from ..common.pattern import is_disabled from ..common.validators import seps_before, seps_after from ...config import load_config_patterns audio_properties = ['audio_codec', 'audio_profile', 'audio_channels'] def audio_codec(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk() \ .regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]) \ .string_defaults(ignore_case=True) def audio_codec_priority(match1, match2): """ Gives priority to audio_codec :param match1: :type match1: :param match2: :type match2: :return: :rtype: """ if match1.name == 'audio_codec' and match2.name in ['audio_profile', 'audio_channels']: return match2 if match1.name in ['audio_profile', 'audio_channels'] and match2.name == 'audio_codec': return match1 return '__default__' rebulk.defaults(name='audio_codec', conflict_solver=audio_codec_priority, disabled=lambda context: is_disabled(context, 'audio_codec')) load_config_patterns(rebulk, config.get('audio_codec')) rebulk.defaults(clear=True, name='audio_profile', disabled=lambda context: is_disabled(context, 'audio_profile')) load_config_patterns(rebulk, config.get('audio_profile')) rebulk.defaults(clear=True, name="audio_channels", disabled=lambda context: is_disabled(context, 'audio_channels')) load_config_patterns(rebulk, config.get('audio_channels')) rebulk.rules(DtsHDRule, DtsRule, AacRule, DolbyDigitalRule, AudioValidatorRule, HqConflictRule, AudioChannelsValidatorRule) return rebulk class AudioValidatorRule(Rule): """ Remove audio properties if not surrounded by separators and not next each others """ priority = 64 consequence = RemoveMatch def when(self, matches, context): ret = [] audio_list = matches.range(predicate=lambda match: match.name in audio_properties) for audio in audio_list: if not seps_before(audio): valid_before = matches.range(audio.start - 1, audio.start, lambda match: match.name in audio_properties) if not valid_before: ret.append(audio) continue if not seps_after(audio): valid_after = matches.range(audio.end, audio.end + 1, lambda match: match.name in audio_properties) if not valid_after: ret.append(audio) continue return ret class AudioProfileRule(Rule): """ Abstract rule to validate audio profiles """ priority = 64 dependency = AudioValidatorRule consequence = RemoveMatch def __init__(self, codec): super().__init__() self.codec = codec def enabled(self, context): return not is_disabled(context, 'audio_profile') def when(self, matches, context): profile_list = matches.named('audio_profile', lambda match: 'audio_profile.rule' in match.tags and self.codec in match.tags) ret = [] for profile in profile_list: codec = matches.at_span(profile.span, lambda match: match.name == 'audio_codec' and match.value == self.codec, 0) if not codec: codec = matches.previous(profile, lambda match: match.name == 'audio_codec' and match.value == self.codec) if not codec: codec = matches.next(profile, lambda match: match.name == 'audio_codec' and match.value == self.codec) if not codec: ret.append(profile) if codec: ret.extend(matches.conflicting(profile)) return ret class DtsHDRule(AudioProfileRule): """ Rule to validate DTS-HD profile """ def __init__(self): super().__init__('DTS-HD') class DtsRule(AudioProfileRule): """ Rule to validate DTS profile """ def __init__(self): super().__init__('DTS') class AacRule(AudioProfileRule): """ Rule to validate AAC profile """ def __init__(self): super().__init__('AAC') class DolbyDigitalRule(AudioProfileRule): """ Rule to validate Dolby Digital profile """ def __init__(self): super().__init__('Dolby Digital') class HqConflictRule(Rule): """ Solve conflict between HQ from other property and from audio_profile. """ dependency = [DtsHDRule, DtsRule, AacRule, DolbyDigitalRule] consequence = RemoveMatch def enabled(self, context): return not is_disabled(context, 'audio_profile') def when(self, matches, context): hq_audio = matches.named('audio_profile', lambda m: m.value == 'High Quality') hq_audio_spans = [match.span for match in hq_audio] return matches.named('other', lambda m: m.span in hq_audio_spans) class AudioChannelsValidatorRule(Rule): """ Remove audio_channel if no audio codec as previous match. """ priority = 128 consequence = RemoveMatch def enabled(self, context): return not is_disabled(context, 'audio_channels') def when(self, matches, context): ret = [] for audio_channel in matches.tagged('weak-audio_channels'): valid_before = matches.range(audio_channel.start - 1, audio_channel.start, lambda match: match.name == 'audio_codec') if not valid_before: ret.append(audio_channel) return ret guessit-3.8.0/guessit/rules/properties/bit_rate.py000066400000000000000000000047231453655371700224070ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ video_bit_rate and audio_bit_rate properties """ from rebulk import Rebulk from rebulk.remodule import re from rebulk.rules import Rule, RemoveMatch, RenameMatch from ..common import dash, seps from ..common.pattern import is_disabled from ..common.validators import seps_surround from ...config import load_config_patterns def bit_rate(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: (is_disabled(context, 'audio_bit_rate') and is_disabled(context, 'video_bit_rate'))) rebulk = rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]) rebulk.defaults(name='audio_bit_rate', validator=seps_surround) load_config_patterns(rebulk, config.get('bit_rate')) rebulk.rules(BitRateTypeRule) return rebulk class BitRateTypeRule(Rule): """ Convert audio bit rate guess into video bit rate. """ consequence = [RenameMatch('video_bit_rate'), RemoveMatch] def when(self, matches, context): to_rename = [] to_remove = [] if is_disabled(context, 'audio_bit_rate'): to_remove.extend(matches.named('audio_bit_rate')) else: video_bit_rate_disabled = is_disabled(context, 'video_bit_rate') 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)): after = matches.next(match, index=0, predicate=lambda m: m.name == 'audio_codec') if after and not matches.holes(match.end, after.start, predicate=lambda m: m.value.strip(seps)): bitrate = match.value if bitrate.units == 'Kbps' or (bitrate.units == 'Mbps' and bitrate.magnitude < 10): continue if video_bit_rate_disabled: to_remove.append(match) else: to_rename.append(match) if to_rename or to_remove: return to_rename, to_remove return False guessit-3.8.0/guessit/rules/properties/bonus.py000066400000000000000000000027011453655371700217360ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ bonus property """ from rebulk import Rebulk, AppendMatch, Rule from rebulk.remodule import re from .title import TitleFromPosition from ..common.formatters import cleanup from ..common.pattern import is_disabled from ...config import load_config_patterns def bonus(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'bonus')) rebulk = rebulk.regex_defaults(name='bonus', flags=re.IGNORECASE) load_config_patterns(rebulk, config.get('bonus')) rebulk.rules(BonusTitleRule) return rebulk class BonusTitleRule(Rule): """ Find bonus title after bonus. """ dependency = TitleFromPosition consequence = AppendMatch properties = {'bonus_title': [None]} def when(self, matches, context): # pylint:disable=inconsistent-return-statements bonus_number = matches.named('bonus', lambda match: not match.private, index=0) if bonus_number: filepath = matches.markers.at_match(bonus_number, lambda marker: marker.name == 'path', 0) hole = matches.holes(bonus_number.end, filepath.end + 1, formatter=cleanup, index=0) if hole and hole.value: hole.name = 'bonus_title' return hole guessit-3.8.0/guessit/rules/properties/cd.py000066400000000000000000000012611453655371700211760ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ cd and cd_count properties """ from rebulk import Rebulk from rebulk.remodule import re from ..common import dash from ..common.pattern import is_disabled from ...config import load_config_patterns def cd(config): # pylint:disable=unused-argument,invalid-name """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'cd')) rebulk = rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]) load_config_patterns(rebulk, config) return rebulk guessit-3.8.0/guessit/rules/properties/container.py000066400000000000000000000044321453655371700225750ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ container property """ from rebulk.remodule import re from rebulk import Rebulk from ..common import seps from ..common.pattern import is_disabled from ..common.validators import seps_surround from ...reutils import build_or_pattern def container(config): """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'container')) rebulk = rebulk.regex_defaults(flags=re.IGNORECASE).string_defaults(ignore_case=True) rebulk.defaults(name='container', formatter=lambda value: value.strip(seps), tags=['extension'], conflict_solver=lambda match, other: other if other.name in ('source', 'video_codec') or other.name == 'container' and 'extension' not in other.tags else '__default__') subtitles = config['subtitles'] info = config['info'] videos = config['videos'] torrent = config['torrent'] nzb = config['nzb'] rebulk.regex(r'\.'+build_or_pattern(subtitles)+'$', exts=subtitles, tags=['extension', 'subtitle']) rebulk.regex(r'\.'+build_or_pattern(info)+'$', exts=info, tags=['extension', 'info']) rebulk.regex(r'\.'+build_or_pattern(videos)+'$', exts=videos, tags=['extension', 'video']) rebulk.regex(r'\.'+build_or_pattern(torrent)+'$', exts=torrent, tags=['extension', 'torrent']) rebulk.regex(r'\.'+build_or_pattern(nzb)+'$', exts=nzb, tags=['extension', 'nzb']) rebulk.defaults(clear=True, name='container', validator=seps_surround, formatter=lambda s: s.lower(), conflict_solver=lambda match, other: match if other.name in ('source', 'video_codec') or other.name == 'container' and 'extension' in other.tags else '__default__') rebulk.string(*[sub for sub in subtitles if sub not in ('sub', 'ass')], tags=['subtitle']) rebulk.string(*videos, tags=['video']) rebulk.string(*torrent, tags=['torrent']) rebulk.string(*nzb, tags=['nzb']) return rebulk guessit-3.8.0/guessit/rules/properties/country.py000066400000000000000000000075111453655371700223170ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ country property """ # pylint: disable=no-member import babelfish from rebulk import Rebulk from ..common.pattern import is_disabled from ..common.words import iter_words def country(config, common_words): """ Builder for rebulk object. :param config: rule configuration :type config: dict :param common_words: common words :type common_words: set :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'country')) rebulk = rebulk.defaults(name='country') def find_countries(string, context=None): """ Find countries in given string. """ allowed_countries = context.get('allowed_countries') if context else None return CountryFinder(allowed_countries, common_words).find(string) rebulk.functional(find_countries, #  Prefer language and any other property over country if not US or GB. conflict_solver=lambda match, other: match if other.name != 'language' or match.value not in (babelfish.Country('US'), babelfish.Country('GB')) else other, properties={'country': [None]}, disabled=lambda context: not context.get('allowed_countries')) babelfish.country_converters['guessit'] = GuessitCountryConverter(config['synonyms']) return rebulk class GuessitCountryConverter(babelfish.CountryReverseConverter): # pylint: disable=missing-docstring def __init__(self, synonyms): self.guessit_exceptions = {} for alpha2, synlist in synonyms.items(): for syn in synlist: self.guessit_exceptions[syn.lower()] = alpha2 @property def codes(self): # pylint: disable=missing-docstring return (babelfish.country_converters['name'].codes | frozenset(babelfish.COUNTRIES.values()) | frozenset(self.guessit_exceptions.keys())) def convert(self, alpha2): if alpha2 == 'GB': return 'UK' return str(babelfish.Country(alpha2)) def reverse(self, name): # pylint:disable=arguments-renamed # exceptions come first, as they need to override a potential match # with any of the other guessers try: return self.guessit_exceptions[name.lower()] except KeyError: pass try: return babelfish.Country(name.upper()).alpha2 except ValueError: pass for conv in [babelfish.Country.fromname]: try: return conv(name).alpha2 except babelfish.CountryReverseError: pass raise babelfish.CountryReverseError(name) class CountryFinder: """Helper class to search and return country matches.""" def __init__(self, allowed_countries, common_words): self.allowed_countries = {l.lower() for l in allowed_countries or []} self.common_words = common_words def find(self, string): """Return all matches for country.""" for word_match in iter_words(string.strip().lower()): word = word_match.value if word.lower() in self.common_words: continue try: country_object = babelfish.Country.fromguessit(word) if (country_object.name.lower() in self.allowed_countries or country_object.alpha2.lower() in self.allowed_countries): yield self._to_rebulk_match(word_match, country_object) except babelfish.Error: continue @classmethod def _to_rebulk_match(cls, word, value): return word.span[0], word.span[1], {'value': value} guessit-3.8.0/guessit/rules/properties/crc.py000066400000000000000000000046721453655371700213700ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ crc and uuid properties """ from rebulk.remodule import re from rebulk import Rebulk from ..common.pattern import is_disabled from ..common.validators import seps_surround def crc(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'crc32')) rebulk = rebulk.regex_defaults(flags=re.IGNORECASE) rebulk.defaults(validator=seps_surround) rebulk.regex('(?:[a-fA-F]|[0-9]){8}', name='crc32', conflict_solver=lambda match, other: other if other.name in ['episode', 'season'] else '__default__') rebulk.functional(guess_idnumber, name='uuid', conflict_solver=lambda match, other: match if other.name in ['episode', 'season'] else '__default__') return rebulk _digit = 0 _letter = 1 _other = 2 _idnum = re.compile(r'(?P[a-zA-Z0-9-]{20,})') # 1.0, (0, 0)) def guess_idnumber(string): """ Guess id number function :param string: :type string: :return: :rtype: """ # pylint:disable=invalid-name ret = [] matches = list(_idnum.finditer(string)) for match in matches: result = match.groupdict() switch_count = 0 switch_letter_count = 0 letter_count = 0 last_letter = None last = _letter for c in result['uuid']: if c in '0123456789': ci = _digit elif c in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ': ci = _letter if c != last_letter: switch_letter_count += 1 last_letter = c letter_count += 1 else: ci = _other if ci != last: switch_count += 1 last = ci # only return the result as probable if we alternate often between # char type (more likely for hash values than for common words) switch_ratio = float(switch_count) / len(result['uuid']) letters_ratio = (float(switch_letter_count) / letter_count) if letter_count > 0 else 1 if switch_ratio > 0.4 and letters_ratio > 0.4: ret.append(match.span()) return ret guessit-3.8.0/guessit/rules/properties/date.py000066400000000000000000000071661453655371700215370ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ date, week and year properties """ import re from rebulk import Rebulk, RemoveMatch, Rule from ..common import dash from ..common.date import search_date, valid_year, valid_week from ..common.pattern import is_disabled from ..common.validators import seps_surround from ...reutils import build_or_pattern def date(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk().defaults(validator=seps_surround) rebulk.regex(r"\d{4}", name="year", formatter=int, disabled=lambda context: is_disabled(context, 'year'), conflict_solver=lambda match, other: other if other.name in ('episode', 'season') and len(other.raw) < len(match.raw) else '__default__', validator=lambda match: seps_surround(match) and valid_year(match.value)) rebulk.regex(build_or_pattern(config.get('week_words')) + r"-?(\d{1,2})", name="week", formatter=int, children=True, flags=re.IGNORECASE, abbreviations=[dash], conflict_solver=lambda match, other: other if other.name in ('episode', 'season') and len(other.raw) < len(match.raw) else '__default__', validator=lambda match: seps_surround(match) and valid_week(match.value)) def date_functional(string, context): # pylint:disable=inconsistent-return-statements """ Search for date in the string and retrieves match :param string: :return: """ ret = search_date(string, context.get('date_year_first'), context.get('date_day_first')) if ret: return ret[0], ret[1], {'value': ret[2]} rebulk.functional(date_functional, name="date", properties={'date': [None]}, disabled=lambda context: is_disabled(context, 'date'), conflict_solver=lambda match, other: other if other.name in ('episode', 'season', 'crc32') else '__default__') rebulk.rules(KeepMarkedYearInFilepart) return rebulk class KeepMarkedYearInFilepart(Rule): """ Keep first years marked with [](){} in filepart, or if no year is marked, ensure it won't override titles. """ priority = 64 consequence = RemoveMatch def enabled(self, context): return not is_disabled(context, 'year') def when(self, matches, context): ret = [] if len(matches.named('year')) > 1: for filepart in matches.markers.named('path'): years = matches.range(filepart.start, filepart.end, lambda match: match.name == 'year') if len(years) > 1: group_years = [] ungroup_years = [] for year in years: if matches.markers.at_match(year, lambda marker: marker.name == 'group'): group_years.append(year) else: ungroup_years.append(year) if group_years and ungroup_years: ret.extend(ungroup_years) ret.extend(group_years[1:]) # Keep the first year in marker. elif not group_years: ret.append(ungroup_years[0]) # Keep first year for title. if len(ungroup_years) > 2: ret.extend(ungroup_years[2:]) return ret guessit-3.8.0/guessit/rules/properties/edition.py000066400000000000000000000014671453655371700222530ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ edition property """ from rebulk import Rebulk from rebulk.remodule import re from ..common import dash from ..common.pattern import is_disabled from ..common.validators import seps_surround from ...config import load_config_patterns def edition(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'edition')) rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]).string_defaults(ignore_case=True) rebulk.defaults(name='edition', validator=seps_surround) load_config_patterns(rebulk, config.get('edition')) return rebulk guessit-3.8.0/guessit/rules/properties/episode_title.py000066400000000000000000000255621453655371700234530ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Episode title """ from collections import defaultdict from rebulk import Rebulk, Rule, AppendMatch, RemoveMatch, RenameMatch, POST_PROCESS from ..common import seps, title_seps from ..common.formatters import cleanup from ..common.pattern import is_disabled from ..common.validators import or_ from ..properties.title import TitleFromPosition, TitleBaseRule from ..properties.type import TypeProcessor def episode_title(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ previous_names = ('episode', 'episode_count', 'season', 'season_count', 'date', 'title', 'year') rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'episode_title')) rebulk = rebulk.rules(RemoveConflictsWithEpisodeTitle(previous_names), EpisodeTitleFromPosition(previous_names), AlternativeTitleReplace(previous_names), TitleToEpisodeTitle, Filepart3EpisodeTitle, Filepart2EpisodeTitle, RenameEpisodeTitleWhenMovieType) return rebulk class RemoveConflictsWithEpisodeTitle(Rule): """ Remove conflicting matches that might lead to wrong episode_title parsing. """ priority = 64 consequence = RemoveMatch def __init__(self, previous_names): super().__init__() self.previous_names = previous_names self.next_names = ('streaming_service', 'screen_size', 'source', 'video_codec', 'audio_codec', 'other', 'container') self.affected_if_holes_after = ('part', ) self.affected_names = ('part', 'year') def when(self, matches, context): to_remove = [] for filepart in matches.markers.named('path'): for match in matches.range(filepart.start, filepart.end, predicate=lambda m: m.name in self.affected_names): before = matches.range(filepart.start, match.start, predicate=lambda m: not m.private, index=-1) if not before or before.name not in self.previous_names: continue after = matches.range(match.end, filepart.end, predicate=lambda m: not m.private, index=0) if not after or after.name not in self.next_names: continue group = matches.markers.at_match(match, predicate=lambda m: m.name == 'group', index=0) def has_value_in_same_group(current_match, current_group=group): """Return true if current match has value and belongs to the current group.""" return current_match.value.strip(seps) and ( current_group == matches.markers.at_match(current_match, predicate=lambda mm: mm.name == 'group', index=0) ) holes_before = matches.holes(before.end, match.start, predicate=has_value_in_same_group) holes_after = matches.holes(match.end, after.start, predicate=has_value_in_same_group) if not holes_before and not holes_after: continue if match.name in self.affected_if_holes_after and not holes_after: continue to_remove.append(match) if match.parent: to_remove.append(match.parent) return to_remove class TitleToEpisodeTitle(Rule): """ If multiple different title are found, convert the one following episode number to episode_title. """ dependency = TitleFromPosition def when(self, matches, context): titles = matches.named('title') title_groups = defaultdict(list) for title in titles: title_groups[title.value].append(title) episode_titles = [] if len(title_groups) < 2: return episode_titles for title in titles: if matches.previous(title, lambda match: match.name == 'episode'): episode_titles.append(title) return episode_titles def then(self, matches, when_response, context): for title in when_response: matches.remove(title) title.name = 'episode_title' matches.append(title) class EpisodeTitleFromPosition(TitleBaseRule): """ Add episode title match in existing matches Must run after TitleFromPosition rule. """ dependency = TitleToEpisodeTitle def __init__(self, previous_names): super().__init__('episode_title', ['title']) self.previous_names = previous_names def hole_filter(self, hole, matches): episode = matches.previous(hole, lambda previous: previous.named(*self.previous_names), 0) crc32 = matches.named('crc32') return episode or crc32 def filepart_filter(self, filepart, matches): # Filepart where title was found. if matches.range(filepart.start, filepart.end, lambda match: match.name == 'title'): return True return False def should_remove(self, match, matches, filepart, hole, context): if match.name == 'episode_details': return False return super().should_remove(match, matches, filepart, hole, context) def when(self, matches, context): # pylint:disable=inconsistent-return-statements if matches.named('episode_title'): return return super().when(matches, context) class AlternativeTitleReplace(Rule): """ If alternateTitle was found and title is next to episode, season or date, replace it with episode_title. """ dependency = EpisodeTitleFromPosition consequence = RenameMatch def __init__(self, previous_names): super().__init__() self.previous_names = previous_names def when(self, matches, context): # pylint:disable=inconsistent-return-statements if matches.named('episode_title'): return alternative_title = matches.range(predicate=lambda match: match.name == 'alternative_title', index=0) if alternative_title: main_title = matches.chain_before(alternative_title.start, seps=seps, predicate=lambda match: 'title' in match.tags, index=0) if main_title: episode = matches.previous(main_title, lambda previous: previous.named(*self.previous_names), 0) crc32 = matches.named('crc32') if episode or crc32: return alternative_title def then(self, matches, when_response, context): matches.remove(when_response) when_response.name = 'episode_title' when_response.tags.append('alternative-replaced') matches.append(when_response) class RenameEpisodeTitleWhenMovieType(Rule): """ Rename episode_title by alternative_title when type is movie. """ priority = POST_PROCESS dependency = TypeProcessor consequence = RenameMatch def when(self, matches, context): # pylint:disable=inconsistent-return-statements if matches.named('episode_title', lambda m: 'alternative-replaced' not in m.tags) \ and not matches.named('type', lambda m: m.value == 'episode'): return matches.named('episode_title') def then(self, matches, when_response, context): for match in when_response: matches.remove(match) match.name = 'alternative_title' matches.append(match) class Filepart3EpisodeTitle(Rule): """ If we have at least 3 filepart structured like this: Serie name/SO1/E01-episode_title.mkv AAAAAAAAAA/BBB/CCCCCCCCCCCCCCCCCCCC Serie name/SO1/episode_title-E01.mkv AAAAAAAAAA/BBB/CCCCCCCCCCCCCCCCCCCC If CCCC contains episode and BBB contains seasonNumber Then title is to be found in AAAA. """ consequence = AppendMatch('title') def when(self, matches, context): # pylint:disable=inconsistent-return-statements if matches.tagged('filepart-title'): return fileparts = matches.markers.named('path') if len(fileparts) < 3: return filename = fileparts[-1] directory = fileparts[-2] subdirectory = fileparts[-3] episode_number = matches.range(filename.start, filename.end, lambda match: match.name == 'episode', 0) if episode_number: season = matches.range(directory.start, directory.end, lambda match: match.name == 'season', 0) if season: hole = matches.holes(subdirectory.start, subdirectory.end, ignore=or_(lambda match: 'weak-episode' in match.tags, TitleBaseRule.is_ignored), formatter=cleanup, seps=title_seps, predicate=lambda match: match.value, index=0) if hole: return hole class Filepart2EpisodeTitle(Rule): """ If we have at least 2 filepart structured like this: Serie name SO1/E01-episode_title.mkv AAAAAAAAAAAAA/BBBBBBBBBBBBBBBBBBBBB If BBBB contains episode and AAA contains a hole followed by seasonNumber then title is to be found in AAAA. or Serie name/SO1E01-episode_title.mkv AAAAAAAAAA/BBBBBBBBBBBBBBBBBBBBB If BBBB contains season and episode and AAA contains a hole then title is to be found in AAAA. """ consequence = AppendMatch('title') def when(self, matches, context): # pylint:disable=inconsistent-return-statements if matches.tagged('filepart-title'): return fileparts = matches.markers.named('path') if len(fileparts) < 2: return filename = fileparts[-1] directory = fileparts[-2] episode_number = matches.range(filename.start, filename.end, lambda match: match.name == 'episode', 0) if episode_number: season = (matches.range(directory.start, directory.end, lambda match: match.name == 'season', 0) or matches.range(filename.start, filename.end, lambda match: match.name == 'season', 0)) if season: hole = matches.holes(directory.start, directory.end, ignore=or_(lambda match: 'weak-episode' in match.tags, TitleBaseRule.is_ignored), formatter=cleanup, seps=title_seps, predicate=lambda match: match.value, index=0) if hole: hole.tags.append('filepart-title') return hole guessit-3.8.0/guessit/rules/properties/episodes.py000066400000000000000000001166511453655371700224350ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ episode, season, disc, episode_count, season_count and episode_details properties """ import copy from collections import defaultdict from rebulk import Rebulk, RemoveMatch, Rule, AppendMatch, RenameMatch from rebulk.match import Match from rebulk.remodule import re from rebulk.utils import is_iterable from guessit.rules import match_processors from guessit.rules.common.numeral import parse_numeral, numeral from .title import TitleFromPosition from ..common import dash, alt_dash, seps, seps_no_fs from ..common.formatters import strip from ..common.pattern import is_disabled from ..common.validators import seps_surround, int_coercable, and_ from ...reutils import build_or_pattern def episodes(config): """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ # pylint: disable=too-many-branches,too-many-statements,too-many-locals def is_season_episode_disabled(context): """Whether season and episode rules should be enabled.""" return is_disabled(context, 'episode') or is_disabled(context, 'season') def episodes_season_chain_breaker(matches): """ Break chains if there's more than 100 offset between two neighbor values. :param matches: :type matches: :return: :rtype: """ eps = matches.named('episode') if len(eps) > 1 and abs(eps[-1].value - eps[-2].value) > episode_max_range: return True seasons = matches.named('season') if len(seasons) > 1 and abs(seasons[-1].value - seasons[-2].value) > season_max_range: return True return False def season_episode_conflict_solver(match, other): """ Conflict solver for episode/season patterns :param match: :param other: :return: """ if match.name != other.name: if match.name == 'episode' and other.name == 'year': return match if match.name in ('season', 'episode'): if other.name in ('video_codec', 'audio_codec', 'container', 'date'): return match if (other.name == 'audio_channels' and 'weak-audio_channels' not in other.tags and not match.initiator.children.named(match.name + 'Marker')) or ( other.name == 'screen_size' and not int_coercable(other.raw)): return match if other.name in ('season', 'episode') and match.initiator != other.initiator: if (match.initiator.name in ('weak_episode', 'weak_duplicate') and other.initiator.name in ('weak_episode', 'weak_duplicate')): return '__default__' for current in (match, other): if 'weak-episode' in current.tags or 'x' in current.initiator.raw.lower(): return current return '__default__' def ordering_validator(match): """ Validator for season list. They should be in natural order to be validated. episode/season separated by a weak discrete separator should be consecutive, unless a strong discrete separator or a range separator is present in the chain (1.3&5 is valid, but 1.3-5 is not valid and 1.3.5 is not valid) """ values = match.children.to_dict() if 'season' in values and is_iterable(values['season']): # Season numbers must be in natural order to be validated. if not list(sorted(values['season'])) == values['season']: return False if 'episode' in values and is_iterable(values['episode']): # Season numbers must be in natural order to be validated. if not list(sorted(values['episode'])) == values['episode']: return False def is_consecutive(property_name): """ Check if the property season or episode has valid consecutive values. :param property_name: :type property_name: :return: :rtype: """ previous_match = None valid = True for current_match in match.children.named(property_name): if previous_match: match.children.previous(current_match, lambda m: m.name == property_name + 'Separator') separator = match.children.previous(current_match, lambda m: m.name == property_name + 'Separator', 0) if separator: if separator.raw not in range_separators and separator.raw in weak_discrete_separators: if not 0 < current_match.value - previous_match.value <= max_range_gap + 1: valid = False if separator.raw in strong_discrete_separators: valid = True break previous_match = current_match return valid return is_consecutive('episode') and is_consecutive('season') def validate_roman(match): """ Validate a roman match if surrounded by separators :param match: :type match: :return: :rtype: """ if int_coercable(match.raw): return True return seps_surround(match) season_words = config['season_words'] episode_words = config['episode_words'] of_words = config['of_words'] all_words = config['all_words'] season_markers = config['season_markers'] season_ep_markers = config['season_ep_markers'] disc_markers = config['disc_markers'] episode_markers = config['episode_markers'] range_separators = config['range_separators'] weak_discrete_separators = list(sep for sep in seps_no_fs if sep not in range_separators) strong_discrete_separators = config['discrete_separators'] discrete_separators = strong_discrete_separators + weak_discrete_separators episode_max_range = config['episode_max_range'] season_max_range = config['season_max_range'] max_range_gap = config['max_range_gap'] rebulk = Rebulk() \ .regex_defaults(flags=re.IGNORECASE) \ .string_defaults(ignore_case=True) \ .chain_defaults(chain_breaker=episodes_season_chain_breaker) \ .defaults(private_names=['episodeSeparator', 'seasonSeparator', 'episodeMarker', 'seasonMarker'], formatter={'season': int, 'episode': int, 'version': int, 'count': int}, children=True, private_parent=True, conflict_solver=season_episode_conflict_solver, abbreviations=[alt_dash]) # S01E02, 01x02, S01S02S03 rebulk.chain( tags=['SxxExx'], validate_all=True, validator={'__parent__': and_(seps_surround, ordering_validator)}, disabled=is_season_episode_disabled) \ .defaults(tags=['SxxExx']) \ .regex(build_or_pattern(season_markers, name='seasonMarker') + r'(?P\d+)@?' + build_or_pattern(episode_markers + disc_markers, name='episodeMarker') + r'@?(?P\d+)') \ .repeater('+') \ .regex(build_or_pattern(episode_markers + disc_markers + discrete_separators + range_separators, name='episodeSeparator', escape=True) + r'(?P\d+)').repeater('*') rebulk.chain(tags=['SxxExx'], validate_all=True, validator={'__parent__': and_(seps_surround, ordering_validator)}, disabled=is_season_episode_disabled) \ .defaults(tags=['SxxExx']) \ .regex(r'(?P\d+)@?' + build_or_pattern(season_ep_markers, name='episodeMarker') + r'@?(?P\d+)').repeater('+') rebulk.chain(tags=['SxxExx'], validate_all=True, validator={'__parent__': and_(seps_surround, ordering_validator)}, disabled=is_season_episode_disabled) \ .defaults(tags=['SxxExx']) \ .regex(r'(?P\d+)@?' + build_or_pattern(season_ep_markers, name='episodeMarker') + r'@?(?P\d+)') \ .regex(build_or_pattern(season_ep_markers + discrete_separators + range_separators, name='episodeSeparator', escape=True) + r'(?P\d+)').repeater('*') rebulk.chain(tags=['SxxExx'], validate_all=True, validator={'__parent__': and_(seps_surround, ordering_validator)}, disabled=is_season_episode_disabled) \ .defaults(tags=['SxxExx']) \ .regex(build_or_pattern(season_markers, name='seasonMarker') + r'(?P\d+)') \ .regex('(?PExtras)', name='other', value='Extras', tags=['no-release-group-prefix']).repeater('?') \ .regex(build_or_pattern(season_markers + discrete_separators + range_separators, name='seasonSeparator', escape=True) + r'(?P\d+)').repeater('*') # episode_details property for episode_detail in ('Special', 'Pilot', 'Unaired', 'Final'): rebulk.string(episode_detail, private_parent=False, children=False, value=episode_detail, name='episode_details', disabled=lambda context: is_disabled(context, 'episode_details')) rebulk.defaults(private_names=['episodeSeparator', 'seasonSeparator', 'episodeMarker', 'seasonMarker'], validate_all=True, validator={'__parent__': and_(seps_surround, ordering_validator)}, children=True, private_parent=True, conflict_solver=season_episode_conflict_solver) rebulk.chain(validate_all=True, conflict_solver=season_episode_conflict_solver, formatter={'season': parse_numeral, 'count': parse_numeral}, validator={'__parent__': and_(seps_surround, ordering_validator), 'season': validate_roman, 'count': validate_roman}, disabled=lambda context: context.get('type') == 'movie' or is_disabled(context, 'season')) \ .defaults(formatter={'season': parse_numeral, 'count': parse_numeral}, validator={'season': validate_roman, 'count': validate_roman}, conflict_solver=season_episode_conflict_solver) \ .regex(build_or_pattern(season_words, name='seasonMarker') + '@?(?P' + numeral + ')') \ .regex(r'' + build_or_pattern(of_words) + '@?(?P' + numeral + ')').repeater('?') \ .regex(r'@?' + build_or_pattern(range_separators + discrete_separators + ['@'], name='seasonSeparator', escape=True) + r'@?(?P\d+)').repeater('*') rebulk.defaults(abbreviations=[dash]) rebulk.regex(build_or_pattern(episode_words, name='episodeMarker') + r'-?(?P\d+)' + r'(?:v(?P\d+))?' + r'(?:-?' + build_or_pattern(of_words) + r'-?(?P\d+))?', # Episode 4 disabled=lambda context: context.get('type') == 'episode' or is_disabled(context, 'episode')) rebulk.regex(build_or_pattern(episode_words, name='episodeMarker') + r'-?(?P' + numeral + ')' + r'(?:v(?P\d+))?' + r'(?:-?' + build_or_pattern(of_words) + r'-?(?P\d+))?', # Episode 4 validator={'episode': validate_roman}, formatter={'episode': parse_numeral}, disabled=lambda context: context.get('type') != 'episode' or is_disabled(context, 'episode')) rebulk.regex(r'S?(?P\d+)-?(?:xE|Ex|E|x)-?(?P' + build_or_pattern(all_words) + ')', tags=['SxxExx'], formatter={'other': lambda match: 'Complete'}, disabled=lambda context: is_disabled(context, 'season')) # 12, 13 rebulk.chain(tags=['weak-episode'], disabled=lambda context: context.get('type') == 'movie' or is_disabled(context, 'episode')) \ .defaults(validator=None, tags=['weak-episode']) \ .regex(r'(?P\d{2})') \ .regex(r'v(?P\d+)').repeater('?') \ .regex(r'(?P[x-])(?P\d{2})', abbreviations=None).repeater('*') # 012, 013 rebulk.chain(tags=['weak-episode'], disabled=lambda context: context.get('type') == 'movie' or is_disabled(context, 'episode')) \ .defaults(validator=None, tags=['weak-episode']) \ .regex(r'0(?P\d{1,2})') \ .regex(r'v(?P\d+)').repeater('?') \ .regex(r'(?P[x-])0(?P\d{1,2})', abbreviations=None).repeater('*') # 112, 113 rebulk.chain(tags=['weak-episode'], name='weak_episode', disabled=lambda context: context.get('type') == 'movie' or is_disabled(context, 'episode')) \ .defaults(validator=None, tags=['weak-episode'], name='weak_episode') \ .regex(r'(?P\d{3,4})') \ .regex(r'v(?P\d+)').repeater('?') \ .regex(r'(?P[x-])(?P\d{3,4})', abbreviations=None).repeater('*') # 1, 2, 3 rebulk.chain(tags=['weak-episode'], disabled=lambda context: context.get('type') != 'episode' or is_disabled(context, 'episode')) \ .defaults(validator=None, tags=['weak-episode']) \ .regex(r'(?P\d)') \ .regex(r'v(?P\d+)').repeater('?') \ .regex(r'(?P[x-])(?P\d{1,2})', abbreviations=None).repeater('*') # e112, e113, 1e18, 3e19 rebulk.chain(disabled=lambda context: is_disabled(context, 'episode')) \ .defaults(validator=None) \ .regex(r'(?P\d{1,2})?(?Pe)(?P\d{1,4})') \ .regex(r'v(?P\d+)').repeater('?') \ .regex(r'(?Pe|x|-)(?P\d{1,4})', abbreviations=None).repeater('*') # ep 112, ep113, ep112, ep113 rebulk.chain(disabled=lambda context: is_disabled(context, 'episode')) \ .defaults(validator=None) \ .regex(r'ep-?(?P\d{1,4})') \ .regex(r'v(?P\d+)').repeater('?') \ .regex(r'(?Pep|e|x|-)(?P\d{1,4})', abbreviations=None).repeater('*') # cap 112, cap 112_114 rebulk.chain(tags=['see-pattern'], disabled=is_season_episode_disabled) \ .defaults(validator=None, tags=['see-pattern']) \ .regex(r'(?Pcap)-?(?P\d{1,2})(?P\d{2})') \ .regex(r'(?P-)(?P\d{1,2})(?P\d{2})').repeater('?') # 102, 0102 rebulk.chain(tags=['weak-episode', 'weak-duplicate'], name='weak_duplicate', conflict_solver=season_episode_conflict_solver, disabled=lambda context: (context.get('episode_prefer_number', False) or context.get('type') == 'movie') or is_season_episode_disabled(context)) \ .defaults(tags=['weak-episode', 'weak-duplicate'], name='weak_duplicate', validator=None, conflict_solver=season_episode_conflict_solver) \ .regex(r'(?P\d{1,2})(?P\d{2})') \ .regex(r'v(?P\d+)').repeater('?') \ .regex(r'(?Px|-)(?P\d{2})', abbreviations=None).repeater('*') rebulk.regex(r'v(?P\d+)', formatter=int, disabled=lambda context: is_disabled(context, 'version')) rebulk.defaults(private_names=['episodeSeparator', 'seasonSeparator']) # detached of X count (season/episode) rebulk.regex(r'(?P\d+)-?' + build_or_pattern(of_words) + r'-?(?P\d+)-?' + build_or_pattern(episode_words) + '?', formatter=int, pre_match_processor=match_processors.strip, disabled=lambda context: is_disabled(context, 'episode')) rebulk.regex(r'Minisodes?', children=False, private_parent=False, name='episode_format', value="Minisode", disabled=lambda context: is_disabled(context, 'episode_format')) rebulk.rules(WeakConflictSolver, RemoveInvalidSeason, RemoveInvalidEpisode, SeePatternRange(range_separators + ['_']), EpisodeNumberSeparatorRange(range_separators), SeasonSeparatorRange(range_separators), RemoveWeakIfMovie, RemoveWeakIfSxxExx, RemoveWeakDuplicate, EpisodeDetailValidator, RemoveDetachedEpisodeNumber, VersionValidator, RemoveWeak(episode_words), RenameToAbsoluteEpisode, CountValidator, EpisodeSingleDigitValidator, RenameToDiscMatch) return rebulk class WeakConflictSolver(Rule): """ Rule to decide whether weak-episode or weak-duplicate matches should be kept. If an anime is detected: - weak-duplicate matches should be removed - weak-episode matches should be tagged as anime Otherwise: - weak-episode matches are removed unless they're part of an episode range match. """ priority = 128 consequence = [RemoveMatch, AppendMatch] def enabled(self, context): return context.get('type') != 'movie' @classmethod def is_anime(cls, matches): """Return True if it seems to be an anime. Anime characteristics: - version, crc32 matches - screen_size inside brackets - release_group at start and inside brackets """ if matches.named('version') or matches.named('crc32'): return True for group in matches.markers.named('group'): if matches.range(group.start, group.end, predicate=lambda m: m.name == 'screen_size'): return True if matches.markers.starting(group.start, predicate=lambda m: m.name == 'path'): hole = matches.holes(group.start, group.end, index=0) if hole and hole.raw == group.raw: return True return False def when(self, matches, context): to_remove = [] to_append = [] anime_detected = self.is_anime(matches) for filepart in matches.markers.named('path'): weak_matches = matches.range(filepart.start, filepart.end, predicate=( lambda m: m.initiator.name == 'weak_episode')) weak_dup_matches = matches.range(filepart.start, filepart.end, predicate=( lambda m: m.initiator.name == 'weak_duplicate')) if anime_detected: if weak_matches: to_remove.extend(weak_dup_matches) for match in matches.range(filepart.start, filepart.end, predicate=( lambda m: m.name == 'episode' and m.initiator.name != 'weak_duplicate')): episode = copy.copy(match) episode.tags = episode.tags + ['anime'] to_append.append(episode) to_remove.append(match) elif weak_dup_matches: episodes_in_range = matches.range(filepart.start, filepart.end, predicate=( lambda m: m.name == 'episode' and m.initiator.name == 'weak_episode' and m.initiator.children.named('episodeSeparator') )) if not episodes_in_range and not matches.range(filepart.start, filepart.end, predicate=lambda m: 'SxxExx' in m.tags): to_remove.extend(weak_matches) else: for match in episodes_in_range: episode = copy.copy(match) episode.tags = [] to_append.append(episode) to_remove.append(match) if to_append: to_remove.extend(weak_dup_matches) if to_remove or to_append: return to_remove, to_append return False class CountValidator(Rule): """ Validate count property and rename it """ priority = 64 consequence = [RemoveMatch, RenameMatch('episode_count'), RenameMatch('season_count')] properties = {'episode_count': [None], 'season_count': [None]} def when(self, matches, context): to_remove = [] episode_count = [] season_count = [] for count in matches.named('count'): previous = matches.previous(count, lambda match: match.name in ['episode', 'season'], 0) if previous: if previous.name == 'episode': episode_count.append(count) elif previous.name == 'season': season_count.append(count) else: to_remove.append(count) if to_remove or episode_count or season_count: return to_remove, episode_count, season_count return False class SeePatternRange(Rule): """ Create matches for episode range for SEE pattern. E.g.: Cap.102_104 """ priority = 128 consequence = [RemoveMatch, AppendMatch] def __init__(self, range_separators): super().__init__() self.range_separators = range_separators def when(self, matches, context): to_remove = [] to_append = [] for separator in matches.tagged('see-pattern', lambda m: m.name == 'episodeSeparator'): previous_match = matches.previous(separator, lambda m: m.name == 'episode' and 'see-pattern' in m.tags, 0) next_match = matches.next(separator, lambda m: m.name == 'season' and 'see-pattern' in m.tags, 0) if not next_match: continue next_match = matches.next(next_match, lambda m: m.name == 'episode' and 'see-pattern' in m.tags, 0) if previous_match and next_match and separator.value in self.range_separators: to_remove.append(next_match) for episode_number in range(previous_match.value + 1, next_match.value + 1): match = copy.copy(next_match) match.value = episode_number to_append.append(match) to_remove.append(separator) if to_remove or to_append: return to_remove, to_append return False class AbstractSeparatorRange(Rule): """ Remove separator matches and create matches for season range. """ consequence = [RemoveMatch, AppendMatch] def __init__(self, range_separators, property_name): super().__init__() self.range_separators = range_separators self.property_name = property_name def _can_start_range(self, match): # pylint: disable=unused-argument return True def when(self, matches, context): to_remove = [] to_append = [] for separator in matches.named(self.property_name + 'Separator'): previous_match = matches.previous(separator, lambda m: m.name == self.property_name, 0) next_match = matches.next(separator, lambda m: m.name == self.property_name, 0) initiator = separator.initiator if previous_match and next_match and separator.value in self.range_separators: to_remove.append(next_match) for episode_number in range(previous_match.value + 1, next_match.value): match = copy.copy(next_match) match.value = episode_number initiator.children.append(match) to_append.append(match) to_append.append(next_match) to_remove.append(separator) previous_match = None sorted_matches = sorted(matches.named(self.property_name), key=lambda x: x.span[0]) for next_match in sorted_matches: if not previous_match and not self._can_start_range(next_match): continue if previous_match: separator = matches.input_string[previous_match.initiator.end:next_match.initiator.start] if separator not in self.range_separators: separator = strip(separator) if separator in self.range_separators: initiator = previous_match.initiator for episode_number in range(previous_match.value + 1, next_match.value): match = copy.copy(next_match) match.value = episode_number initiator.children.append(match) to_append.append(match) to_append.append(Match(previous_match.end, next_match.start - 1, name=self.property_name + 'Separator', private=True, input_string=matches.input_string)) to_remove.append(next_match) # Remove and append match to support proper ordering to_append.append(next_match) previous_match = next_match if to_remove or to_append: return to_remove, to_append return False class RenameToAbsoluteEpisode(Rule): """ Rename episode to absolute_episodes. Absolute episodes are only used if two groups of episodes are detected: S02E04-06 25-27 25-27 S02E04-06 2x04-06 25-27 28. Anime Name S02E05 The matches in the group with higher episode values are renamed to absolute_episode. """ consequence = [RenameMatch('absolute_episode'), RemoveMatch] def when(self, matches, context): # pylint:disable=inconsistent-return-statements initiators = {match.initiator for match in matches.named('episode') if len(match.initiator.children.named('episode')) > 1} if len(initiators) != 2: ret = ([], []) for filepart in matches.markers.named('path'): sxxexx_episode_matches = matches.range(filepart.start + 1, filepart.end, predicate=lambda m: m.name == 'episode' and 'SxxExx' in m.tags) if matches.range(filepart.start + 1, filepart.end, predicate=lambda m: m.name == 'episode'): absolute_episode_candidate = matches.starting(filepart.start, predicate=lambda m: m.initiator.name == 'weak_episode') if sxxexx_episode_matches: ret[1].extend(absolute_episode_candidate) else: ret[0].extend(absolute_episode_candidate) return ret initiators = sorted(initiators, key=lambda item: item.end) if not matches.holes(initiators[0].end, initiators[1].start, predicate=lambda m: m.raw.strip(seps)): first_range = matches.named('episode', predicate=lambda m: m.initiator == initiators[0]) second_range = matches.named('episode', predicate=lambda m: m.initiator == initiators[1]) if len(first_range) == len(second_range): if second_range[0].value > first_range[0].value: return second_range, [] if first_range[0].value > second_range[0].value: return first_range, [] class EpisodeNumberSeparatorRange(AbstractSeparatorRange): """ Remove separator matches and create matches for episoderNumber range. """ priority = 128 def __init__(self, range_separators): super().__init__(range_separators, "episode") def _can_start_range(self, match): return 'weak-episode' not in match.tags class SeasonSeparatorRange(AbstractSeparatorRange): """ Remove separator matches and create matches for season range. """ priority = 128 def __init__(self, range_separators): super().__init__(range_separators, "season") class RemoveWeakIfMovie(Rule): """ Remove weak-episode tagged matches if it seems to be a movie. """ priority = 64 consequence = RemoveMatch def enabled(self, context): return context.get('type') != 'episode' def when(self, matches, context): to_remove = [] to_ignore = set() remove = False for filepart in matches.markers.named('path'): year = matches.range(filepart.start, filepart.end, predicate=lambda m: m.name == 'year', index=0) if year: remove = True next_match = matches.range(year.end, filepart.end, predicate=lambda m: m.private, index=0) if (next_match and not matches.holes(year.end, next_match.start, predicate=lambda m: m.raw.strip(seps)) and not matches.at_match(next_match, predicate=lambda m: m.name == 'year')): to_ignore.add(next_match.initiator) to_ignore.update(matches.range(filepart.start, filepart.end, predicate=lambda m: len(m.children.named('episode')) > 1)) to_remove.extend(matches.conflicting(year)) if remove: to_remove.extend(matches.tagged('weak-episode', predicate=( lambda m: m.initiator not in to_ignore and 'anime' not in m.tags))) return to_remove class RemoveWeak(Rule): """ Remove weak-episode matches which appears after video, source, and audio matches. """ priority = 16 consequence = RemoveMatch, AppendMatch def __init__(self, episode_words): super().__init__() self.episode_words = episode_words def when(self, matches, context): to_remove = [] to_append = [] for filepart in matches.markers.named('path'): weaks = matches.range(filepart.start, filepart.end, predicate=lambda m: 'weak-episode' in m.tags) if weaks: weak = weaks[0] previous = matches.previous(weak, predicate=lambda m: m.name in ( 'audio_codec', 'screen_size', 'streaming_service', 'source', 'video_profile', 'audio_channels', 'audio_profile'), index=0) if previous and not matches.holes( previous.end, weak.start, predicate=lambda m: m.raw.strip(seps)): if previous.raw.lower() in self.episode_words: try: episode = copy.copy(weak) episode.name = 'episode' episode.value = int(weak.value) episode.start = previous.start episode.private = False episode.tags = [] to_append.append(episode) except ValueError: pass to_remove.extend(weaks) if to_remove or to_append: return to_remove, to_append return False class RemoveWeakIfSxxExx(Rule): """ Remove weak-episode tagged matches if SxxExx pattern is matched. Weak episodes at beginning of filepart are kept. """ priority = 64 consequence = RemoveMatch def when(self, matches, context): to_remove = [] for filepart in matches.markers.named('path'): if matches.range(filepart.start, filepart.end, predicate=lambda m: not m.private and 'SxxExx' in m.tags): for match in matches.range(filepart.start, filepart.end, predicate=lambda m: 'weak-episode' in m.tags): if match.start != filepart.start or match.initiator.name != 'weak_episode': to_remove.append(match) return to_remove class RemoveInvalidSeason(Rule): """ Remove invalid season matches. """ priority = 64 consequence = RemoveMatch def when(self, matches, context): to_remove = [] for filepart in matches.markers.named('path'): strong_season = matches.range(filepart.start, filepart.end, index=0, predicate=lambda m: m.name == 'season' and not m.private and 'SxxExx' in m.tags) if strong_season: if strong_season.initiator.children.named('episode'): for season in matches.range(strong_season.end, filepart.end, predicate=lambda m: m.name == 'season' and not m.private): # remove weak season or seasons without episode matches if 'SxxExx' not in season.tags or not season.initiator.children.named('episode'): if season.initiator: to_remove.append(season.initiator) to_remove.extend(season.initiator.children) else: to_remove.append(season) return to_remove class RemoveInvalidEpisode(Rule): """ Remove invalid episode matches. """ priority = 64 consequence = RemoveMatch def when(self, matches, context): to_remove = [] for filepart in matches.markers.named('path'): strong_episode = matches.range(filepart.start, filepart.end, index=0, predicate=lambda m: m.name == 'episode' and not m.private and 'SxxExx' in m.tags) if strong_episode: strong_ep_marker = RemoveInvalidEpisode.get_episode_prefix(matches, strong_episode) for episode in matches.range(strong_episode.end, filepart.end, predicate=lambda m: m.name == 'episode' and not m.private): ep_marker = RemoveInvalidEpisode.get_episode_prefix(matches, episode) if strong_ep_marker and ep_marker and strong_ep_marker.value.lower() != ep_marker.value.lower(): if episode.initiator: to_remove.append(episode.initiator) to_remove.extend(episode.initiator.children) else: to_remove.append(ep_marker) to_remove.append(episode) return to_remove @staticmethod def get_episode_prefix(matches, episode): """ Return episode prefix: episodeMarker or episodeSeparator """ return matches.previous(episode, index=0, predicate=lambda m: m.name in ('episodeMarker', 'episodeSeparator')) class RemoveWeakDuplicate(Rule): """ Remove weak-duplicate tagged matches if duplicate patterns, for example The 100.109 """ priority = 64 consequence = RemoveMatch def when(self, matches, context): to_remove = [] for filepart in matches.markers.named('path'): patterns = defaultdict(list) for match in reversed(matches.range(filepart.start, filepart.end, predicate=lambda m: 'weak-duplicate' in m.tags)): if match.pattern in patterns[match.name]: to_remove.append(match) else: patterns[match.name].append(match.pattern) return to_remove class EpisodeDetailValidator(Rule): """ Validate episode_details if they are detached or next to season or episode. """ priority = 64 consequence = RemoveMatch def when(self, matches, context): ret = [] for detail in matches.named('episode_details'): if not seps_surround(detail) \ and not matches.previous(detail, lambda match: match.name in ['season', 'episode']) \ and not matches.next(detail, lambda match: match.name in ['season', 'episode']): ret.append(detail) return ret class RemoveDetachedEpisodeNumber(Rule): """ If multiple episode are found, remove those that are not detached from a range and less than 10. Fairy Tail 2 - 16-20, 2 should be removed. """ priority = 64 consequence = RemoveMatch dependency = [RemoveWeakIfSxxExx, RemoveWeakDuplicate] def when(self, matches, context): ret = [] episode_numbers = [] episode_values = set() for match in matches.named('episode', lambda m: not m.private and 'weak-episode' in m.tags): if match.value not in episode_values: episode_numbers.append(match) episode_values.add(match.value) episode_numbers = list(sorted(episode_numbers, key=lambda m: m.value)) if len(episode_numbers) > 1 and \ episode_numbers[0].value < 10 and \ episode_numbers[1].value - episode_numbers[0].value != 1: parent = episode_numbers[0] while parent: ret.append(parent) parent = parent.parent return ret class VersionValidator(Rule): """ Validate version if previous match is episode or if surrounded by separators. """ priority = 64 dependency = [RemoveWeakIfMovie, RemoveWeakIfSxxExx] consequence = RemoveMatch def when(self, matches, context): ret = [] for version in matches.named('version'): episode_number = matches.previous(version, lambda match: match.name == 'episode', 0) if not episode_number and not seps_surround(version.initiator): ret.append(version) return ret class EpisodeSingleDigitValidator(Rule): """ Remove single digit episode when inside a group that doesn't own title. """ dependency = [TitleFromPosition] consequence = RemoveMatch def when(self, matches, context): ret = [] for episode in matches.named('episode', lambda match: len(match.initiator) == 1): group = matches.markers.at_match(episode, lambda marker: marker.name == 'group', index=0) if group: if not matches.range(*group.span, predicate=lambda match: match.name == 'title'): ret.append(episode) return ret class RenameToDiscMatch(Rule): """ Rename episodes detected with `d` episodeMarkers to `disc`. """ consequence = [RenameMatch('disc'), RenameMatch('discMarker'), RemoveMatch] def when(self, matches, context): discs = [] markers = [] to_remove = [] disc_disabled = is_disabled(context, 'disc') for marker in matches.named('episodeMarker', predicate=lambda m: m.value.lower() == 'd'): if disc_disabled: to_remove.append(marker) to_remove.extend(marker.initiator.children) continue markers.append(marker) discs.extend(sorted(marker.initiator.children.named('episode'), key=lambda m: m.value)) if discs or markers or to_remove: return discs, markers, to_remove return False guessit-3.8.0/guessit/rules/properties/film.py000066400000000000000000000031101453655371700215320ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ film property """ from rebulk import Rebulk, AppendMatch, Rule from rebulk.remodule import re from ..common import dash from ..common.formatters import cleanup from ..common.pattern import is_disabled from ..common.validators import seps_surround from ...config import load_config_patterns def film(config): # pylint:disable=unused-argument """ Builder for rebulk object. :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'film')) rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]).string_defaults(ignore_case=True) rebulk.defaults(name='film', validator=seps_surround) load_config_patterns(rebulk, config.get('film')) rebulk.rules(FilmTitleRule) return rebulk class FilmTitleRule(Rule): """ Rule to find out film_title (hole after film property """ consequence = AppendMatch properties = {'film_title': [None]} def enabled(self, context): return not is_disabled(context, 'film_title') def when(self, matches, context): # pylint:disable=inconsistent-return-statements bonus_number = matches.named('film', lambda match: not match.private, index=0) if bonus_number: filepath = matches.markers.at_match(bonus_number, lambda marker: marker.name == 'path', 0) hole = matches.holes(filepath.start, bonus_number.start + 1, formatter=cleanup, index=0) if hole and hole.value: hole.name = 'film_title' return hole guessit-3.8.0/guessit/rules/properties/language.py000066400000000000000000000476401453655371700224060ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ language and subtitle_language properties """ # pylint: disable=no-member import copy from collections import defaultdict, namedtuple import babelfish from rebulk import Rebulk, Rule, RemoveMatch, RenameMatch from rebulk.remodule import re from ..common import seps from ..common.pattern import is_disabled from ..common.validators import seps_surround from ..common.words import iter_words def language(config, common_words): """ Builder for rebulk object. :param config: rule configuration :type config: dict :param common_words: common words :type common_words: set :return: Created Rebulk object :rtype: Rebulk """ subtitle_both = config['subtitle_affixes'] subtitle_prefixes = sorted(subtitle_both + config['subtitle_prefixes'], key=length_comparator) subtitle_suffixes = sorted(subtitle_both + config['subtitle_suffixes'], key=length_comparator) lang_both = config['language_affixes'] lang_prefixes = sorted(lang_both + config['language_prefixes'], key=length_comparator) lang_suffixes = sorted(lang_both + config['language_suffixes'], key=length_comparator) weak_affixes = frozenset(config['weak_affixes']) rebulk = Rebulk(disabled=lambda context: (is_disabled(context, 'language') and is_disabled(context, 'subtitle_language'))) rebulk.string(*subtitle_prefixes, name="subtitle_language.prefix", ignore_case=True, private=True, validator=seps_surround, tags=['release-group-prefix'], disabled=lambda context: is_disabled(context, 'subtitle_language')) rebulk.string(*subtitle_suffixes, name="subtitle_language.suffix", ignore_case=True, private=True, validator=seps_surround, disabled=lambda context: is_disabled(context, 'subtitle_language')) rebulk.string(*lang_suffixes, name="language.suffix", ignore_case=True, private=True, validator=seps_surround, tags=['source-suffix'], disabled=lambda context: is_disabled(context, 'language')) def find_languages(string, context=None): """Find languages in the string :return: list of tuple (property, Language, lang_word, word) """ return LanguageFinder(context, subtitle_prefixes, subtitle_suffixes, lang_prefixes, lang_suffixes, weak_affixes).find(string) rebulk.functional(find_languages, properties={'language': [None]}, disabled=lambda context: not context.get('allowed_languages')) rebulk.rules(SubtitleExtensionRule, SubtitlePrefixLanguageRule, SubtitleSuffixLanguageRule, RemoveLanguage, RemoveInvalidLanguages(common_words), RemoveUndeterminedLanguages) babelfish.language_converters['guessit'] = GuessitConverter(config['synonyms']) return rebulk UNDETERMINED = babelfish.Language('und') MULTIPLE = babelfish.Language('mul') NON_SPECIFIC_LANGUAGES = frozenset([UNDETERMINED, MULTIPLE]) class GuessitConverter(babelfish.LanguageReverseConverter): # pylint: disable=missing-docstring _with_country_regexp = re.compile(r'(.*)\((.*)\)') _with_country_regexp2 = re.compile(r'(.*)-(.*)') def __init__(self, synonyms): self.guessit_exceptions = {} for code, synlist in synonyms.items(): if '_' in code: (alpha3, country) = code.split('_') else: (alpha3, country) = (code, None) for syn in synlist: self.guessit_exceptions[syn.lower()] = (alpha3, country, None) @property def codes(self): # pylint: disable=missing-docstring return (babelfish.language_converters['alpha3b'].codes | babelfish.language_converters['alpha2'].codes | babelfish.language_converters['name'].codes | babelfish.language_converters['opensubtitles'].codes | babelfish.country_converters['name'].codes | frozenset(self.guessit_exceptions.keys())) def convert(self, alpha3, country=None, script=None): return str(babelfish.Language(alpha3, country, script)) def reverse(self, name): # pylint:disable=arguments-renamed name = name.lower() # exceptions come first, as they need to override a potential match # with any of the other guessers try: return self.guessit_exceptions[name] except KeyError: pass for conv in [babelfish.Language, babelfish.Language.fromalpha3b, babelfish.Language.fromalpha2, babelfish.Language.fromname, babelfish.Language.fromopensubtitles, babelfish.Language.fromietf]: try: reverse = conv(name) return reverse.alpha3, reverse.country, reverse.script except (ValueError, babelfish.LanguageReverseError): pass raise babelfish.LanguageReverseError(name) def length_comparator(value): """ Return value length. """ return len(value) _LanguageMatch = namedtuple('_LanguageMatch', ['property_name', 'word', 'lang']) class LanguageWord: """ Extension to the Word namedtuple in order to create compound words. E.g.: pt-BR, soft subtitles, custom subs """ def __init__(self, start, end, value, input_string, next_word=None): self.start = start self.end = end self.value = value self.input_string = input_string self.next_word = next_word @property def extended_word(self): # pylint:disable=inconsistent-return-statements """ Return the extended word for this instance, if any. """ if self.next_word: separator = self.input_string[self.end:self.next_word.start] next_separator = self.input_string[self.next_word.end:self.next_word.end + 1] if (separator == '-' and separator != next_separator) or separator in (' ', '.'): value = self.input_string[self.start:self.next_word.end].replace('.', ' ') return LanguageWord(self.start, self.next_word.end, value, self.input_string, self.next_word.next_word) def __repr__(self): return f'<({self.start},{self.end}): {self.value}' def to_rebulk_match(language_match): """ Convert language match to rebulk Match: start, end, dict """ word = language_match.word start = word.start end = word.end name = language_match.property_name if language_match.lang == UNDETERMINED: return start, end, { 'name': name, 'value': word.value.lower(), 'formatter': babelfish.Language, 'tags': ['weak-language'] } return start, end, { 'name': name, 'value': language_match.lang } class LanguageFinder: """ Helper class to search and return language matches: 'language' and 'subtitle_language' properties """ def __init__(self, context, subtitle_prefixes, subtitle_suffixes, lang_prefixes, lang_suffixes, weak_affixes): allowed_languages = context.get('allowed_languages') if context else None self.allowed_languages = {l.lower() for l in allowed_languages or []} self.weak_affixes = weak_affixes self.prefixes_map = {} self.suffixes_map = {} if not is_disabled(context, 'subtitle_language'): self.prefixes_map['subtitle_language'] = subtitle_prefixes self.suffixes_map['subtitle_language'] = subtitle_suffixes self.prefixes_map['language'] = lang_prefixes self.suffixes_map['language'] = lang_suffixes def find(self, string): """ Return all matches for language and subtitle_language. Undetermined language matches are removed if a regular language is found. Multi language matches are removed if there are only undetermined language matches """ regular_lang_map = defaultdict(set) undetermined_map = defaultdict(set) multi_map = defaultdict(set) for match in self.iter_language_matches(string): key = match.property_name if match.lang == UNDETERMINED: undetermined_map[key].add(match) elif match.lang == MULTIPLE: multi_map[key].add(match) else: regular_lang_map[key].add(match) for key, values in multi_map.items(): if key in regular_lang_map or key not in undetermined_map: for value in values: yield to_rebulk_match(value) for key, values in undetermined_map.items(): if key not in regular_lang_map: for value in values: yield to_rebulk_match(value) for values in regular_lang_map.values(): for value in values: yield to_rebulk_match(value) def iter_language_matches(self, string): """ Return language matches for the given string. """ candidates = [] previous = None for word in iter_words(string): language_word = LanguageWord(start=word.span[0], end=word.span[1], value=word.value, input_string=string) if previous: previous.next_word = language_word candidates.append(previous) previous = language_word if previous: candidates.append(previous) for candidate in candidates: for match in self.iter_matches_for_candidate(candidate): yield match def iter_matches_for_candidate(self, language_word): """ Return language matches for the given candidate word. """ tuples = [ (language_word, language_word.next_word, self.prefixes_map, lambda string, prefix: string.startswith(prefix), lambda string, prefix: string[len(prefix):]), (language_word.next_word, language_word, self.suffixes_map, lambda string, suffix: string.endswith(suffix), lambda string, suffix: string[:len(string) - len(suffix)]) ] for word, fallback_word, affixes, is_affix, strip_affix in tuples: if not word: continue match = self.find_match_for_word(word, fallback_word, affixes, is_affix, strip_affix) if match: yield match match = self.find_language_match_for_word(language_word) if match: yield match def find_match_for_word(self, word, fallback_word, affixes, is_affix, strip_affix): """ Return the language match for the given word and affixes. """ for current_word in (word.extended_word, word): if not current_word: continue word_lang = current_word.value.lower() for key, parts in affixes.items(): for part in parts: if not is_affix(word_lang, part): continue match = None value = strip_affix(word_lang, part) if not value: if fallback_word and ( abs(fallback_word.start - word.end) <= 1 or abs(word.start - fallback_word.end) <= 1): match = self.find_language_match_for_word(fallback_word, key=key) if not match and part not in self.weak_affixes: match = self.create_language_match(key, LanguageWord(current_word.start, current_word.end, 'und', current_word.input_string)) else: match = self.create_language_match(key, LanguageWord(current_word.start, current_word.end, value, current_word.input_string)) if match: return match return None def find_language_match_for_word(self, word, key='language'): # pylint:disable=inconsistent-return-statements """ Return the language match for the given word. """ for current_word in (word.extended_word, word): if current_word: match = self.create_language_match(key, current_word) if match: return match def create_language_match(self, key, word): # pylint:disable=inconsistent-return-statements """ Create a LanguageMatch for a given word """ lang = self.parse_language(word.value.lower()) if lang is not None: return _LanguageMatch(property_name=key, word=word, lang=lang) def parse_language(self, lang_word): # pylint:disable=inconsistent-return-statements """ Parse the lang_word into a valid Language. Multi and Undetermined languages are also valid languages. """ try: lang = babelfish.Language.fromguessit(lang_word) if ((hasattr(lang, 'name') and lang.name.lower() in self.allowed_languages) or (hasattr(lang, 'alpha2') and lang.alpha2.lower() in self.allowed_languages) or lang.alpha3.lower() in self.allowed_languages): return lang except babelfish.Error: pass class SubtitlePrefixLanguageRule(Rule): """ Convert language guess as subtitle_language if previous match is a subtitle language prefix """ consequence = RemoveMatch properties = {'subtitle_language': [None]} def enabled(self, context): return not is_disabled(context, 'subtitle_language') def when(self, matches, context): to_rename = [] to_remove = matches.named('subtitle_language.prefix') for lang in matches.named('language'): prefix = matches.previous(lang, lambda match: match.name == 'subtitle_language.prefix', 0) if not prefix: group_marker = matches.markers.at_match(lang, lambda marker: marker.name == 'group', 0) if group_marker: # Find prefix if placed just before the group prefix = matches.previous(group_marker, lambda match: match.name == 'subtitle_language.prefix', 0) if not prefix: # Find prefix if placed before in the group prefix = matches.range(group_marker.start, lang.start, lambda match: match.name == 'subtitle_language.prefix', 0) if prefix: to_rename.append((prefix, lang)) to_remove.extend(matches.conflicting(lang)) if prefix in to_remove: to_remove.remove(prefix) if to_rename or to_remove: return to_rename, to_remove return False def then(self, matches, when_response, context): to_rename, to_remove = when_response super().then(matches, to_remove, context) for prefix, match in to_rename: # Remove suffix equivalent of prefix. suffix = copy.copy(prefix) suffix.name = 'subtitle_language.suffix' if suffix in matches: matches.remove(suffix) matches.remove(match) match.name = 'subtitle_language' matches.append(match) class SubtitleSuffixLanguageRule(Rule): """ Convert language guess as subtitle_language if next match is a subtitle language suffix """ dependency = SubtitlePrefixLanguageRule consequence = RemoveMatch properties = {'subtitle_language': [None]} def enabled(self, context): return not is_disabled(context, 'subtitle_language') def when(self, matches, context): to_append = [] to_remove = matches.named('subtitle_language.suffix') for lang in matches.named('language'): suffix = matches.next(lang, lambda match: match.name == 'subtitle_language.suffix', 0) if suffix: to_append.append(lang) if suffix in to_remove: to_remove.remove(suffix) if to_append or to_remove: return to_append, to_remove return False def then(self, matches, when_response, context): to_rename, to_remove = when_response super().then(matches, to_remove, context) for match in to_rename: matches.remove(match) match.name = 'subtitle_language' matches.append(match) class SubtitleExtensionRule(Rule): """ Convert language guess as subtitle_language if next match is a subtitle extension. Since it's a strong match, it also removes any conflicting source with it. """ consequence = [RemoveMatch, RenameMatch('subtitle_language')] properties = {'subtitle_language': [None]} def enabled(self, context): return not is_disabled(context, 'subtitle_language') def when(self, matches, context): # pylint:disable=inconsistent-return-statements subtitle_extension = matches.named('container', lambda match: 'extension' in match.tags and 'subtitle' in match.tags, 0) if subtitle_extension: subtitle_lang = matches.previous(subtitle_extension, lambda match: match.name == 'language', 0) if subtitle_lang: for weak in matches.named('subtitle_language', predicate=lambda m: 'weak-language' in m.tags): weak.private = True return matches.conflicting(subtitle_lang, lambda m: m.name == 'source'), subtitle_lang class RemoveLanguage(Rule): """Remove language matches that were not converted to subtitle_language when language is disabled.""" consequence = RemoveMatch def enabled(self, context): return is_disabled(context, 'language') def when(self, matches, context): return matches.named('language') class RemoveInvalidLanguages(Rule): """Remove language matches that matches the blacklisted common words.""" consequence = RemoveMatch priority = 32 def __init__(self, common_words): """Constructor.""" super().__init__() self.common_words = common_words def when(self, matches, context): to_remove = [] for match in matches.range(0, len(matches.input_string), predicate=lambda m: m.name in ('language', 'subtitle_language')): if match.raw.lower() not in self.common_words: continue group = matches.markers.at_match(match, index=0, predicate=lambda m: m.name == 'group') if group and ( not matches.range( group.start, group.end, predicate=lambda m: m.name not in ('language', 'subtitle_language') ) and (not matches.holes(group.start, group.end, predicate=lambda m: m.value.strip(seps)))): continue to_remove.append(match) return to_remove class RemoveUndeterminedLanguages(Rule): """Remove "und" language matches when next other language if found.""" consequence = RemoveMatch priority = 32 def when(self, matches, context): to_remove = [] for match in matches.range(0, len(matches.input_string), predicate=lambda m: m.name in ('language', 'subtitle_language')): if match.value == "und": previous = matches.previous(match, index=0) next_ = matches.next(match, index=0) if previous and previous.name == 'language' or next_ and next_.name == 'language': to_remove.append(match) return to_remove guessit-3.8.0/guessit/rules/properties/mimetype.py000066400000000000000000000023601453655371700224420ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ mimetype property """ import mimetypes from rebulk import Rebulk, CustomRule, POST_PROCESS from rebulk.match import Match from ..common.pattern import is_disabled from ...rules.processors import Processors def mimetype(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'mimetype')) rebulk.rules(Mimetype) return rebulk class Mimetype(CustomRule): """ Mimetype post processor :param matches: :type matches: :return: :rtype: """ priority = POST_PROCESS dependency = Processors def when(self, matches, context): mime, _ = mimetypes.guess_type(matches.input_string, strict=False) return mime def then(self, matches, when_response, context): mime = when_response matches.append(Match(len(matches.input_string), len(matches.input_string), name='mimetype', value=mime)) @property def properties(self): """ Properties for this rule. """ return {'mimetype': [None]} guessit-3.8.0/guessit/rules/properties/other.py000066400000000000000000000272531453655371700217420ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ other property """ from rebulk import Rebulk, Rule, RemoveMatch, RenameMatch, POST_PROCESS, AppendMatch from rebulk.match import Match from rebulk.remodule import re from ..common import dash from ..common import seps from ..common.pattern import is_disabled from ..common.validators import seps_after, seps_before, seps_surround, and_ from ...config import load_config_patterns from ...reutils import build_or_pattern from ...rules.common.formatters import raw_cleanup def other(config): # pylint:disable=unused-argument,too-many-statements """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'other')) rebulk = rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]).string_defaults(ignore_case=True) rebulk.defaults(name="other", validator=seps_surround) load_config_patterns(rebulk, config.get('other')) rebulk.rules(RenameAnotherToOther, ValidateHasNeighbor, ValidateHasNeighborAfter, ValidateHasNeighborBefore, ValidateScreenerRule, ValidateMuxRule, ValidateHardcodedSubs, ValidateStreamingServiceNeighbor, ValidateAtEnd, ValidateReal, ProperCountRule) return rebulk def complete_words(rebulk: Rebulk, season_words, complete_article_words): """ Custom pattern to find complete seasons from words. """ season_words_pattern = build_or_pattern(season_words) complete_article_words_pattern = build_or_pattern(complete_article_words) def validate_complete(match): """ Make sure season word is are defined. :param match: :type match: :return: :rtype: """ children = match.children if not children.named('completeWordsBefore') and not children.named('completeWordsAfter'): return False return True rebulk.regex('(?P' + complete_article_words_pattern + '-)?' + '(?P' + season_words_pattern + '-)?' + 'Complete' + '(?P-' + season_words_pattern + ')?', private_names=['completeArticle', 'completeWordsBefore', 'completeWordsAfter'], value={'other': 'Complete'}, tags=['release-group-prefix'], validator={'__parent__': and_(seps_surround, validate_complete)}) class ProperCountRule(Rule): """ Add proper_count property """ priority = POST_PROCESS consequence = AppendMatch properties = {'proper_count': [None]} def when(self, matches, context): # pylint:disable=inconsistent-return-statements propers = matches.named('other', lambda match: match.value == 'Proper') if propers: raws = {} # Count distinct raw values for proper in propers: raws[raw_cleanup(proper.raw)] = proper value = 0 start = None end = None proper_count_matches = [] for proper in raws.values(): if not start or start > proper.start: start = proper.start if not end or end < proper.end: end = proper.end if proper.children.named('proper_count', 0): value += int(proper.children.named('proper_count', 0).value) elif 'real' in proper.tags: value += 2 else: value += 1 proper_count_match = Match(name='proper_count', start=start, end=end, input_string=matches.input_string) proper_count_match.value = value proper_count_matches.append(proper_count_match) return proper_count_matches class RenameAnotherToOther(Rule): """ Rename `another` properties to `other` """ priority = 32 consequence = RenameMatch('other') def when(self, matches, context): return matches.named('another') class ValidateHasNeighbor(Rule): """ Validate tag has-neighbor """ consequence = RemoveMatch priority = 64 def when(self, matches, context): ret = [] for to_check in matches.range(predicate=lambda match: 'has-neighbor' in match.tags): previous_match = matches.previous(to_check, index=0) previous_group = matches.markers.previous(to_check, lambda marker: marker.name == 'group', 0) if previous_group and (not previous_match or previous_group.end > previous_match.end): previous_match = previous_group if previous_match and not matches.input_string[previous_match.end:to_check.start].strip(seps): break next_match = matches.next(to_check, index=0) next_group = matches.markers.next(to_check, lambda marker: marker.name == 'group', 0) if next_group and (not next_match or next_group.start < next_match.start): next_match = next_group if next_match and not matches.input_string[to_check.end:next_match.start].strip(seps): break ret.append(to_check) return ret class ValidateHasNeighborBefore(Rule): """ Validate tag has-neighbor-before that previous match exists. """ consequence = RemoveMatch priority = 64 def when(self, matches, context): ret = [] for to_check in matches.range(predicate=lambda match: 'has-neighbor-before' in match.tags): next_match = matches.next(to_check, index=0) next_group = matches.markers.next(to_check, lambda marker: marker.name == 'group', 0) if next_group and (not next_match or next_group.start < next_match.start): next_match = next_group if next_match and not matches.input_string[to_check.end:next_match.start].strip(seps): break ret.append(to_check) return ret class ValidateHasNeighborAfter(Rule): """ Validate tag has-neighbor-after that next match exists. """ consequence = RemoveMatch priority = 64 def when(self, matches, context): ret = [] for to_check in matches.range(predicate=lambda match: 'has-neighbor-after' in match.tags): previous_match = matches.previous(to_check, index=0) previous_group = matches.markers.previous(to_check, lambda marker: marker.name == 'group', 0) if previous_group and (not previous_match or previous_group.end > previous_match.end): previous_match = previous_group if previous_match and not matches.input_string[previous_match.end:to_check.start].strip(seps): break ret.append(to_check) return ret class ValidateScreenerRule(Rule): """ Validate tag other.validate.screener """ consequence = RemoveMatch priority = 64 def when(self, matches, context): ret = [] for screener in matches.named('other', lambda match: 'other.validate.screener' in match.tags): source_match = matches.previous(screener, lambda match: match.initiator.name == 'source', 0) if not source_match or matches.input_string[source_match.end:screener.start].strip(seps): ret.append(screener) return ret class ValidateMuxRule(Rule): """ Validate tag other.validate.mux """ consequence = RemoveMatch priority = 64 def when(self, matches, context): ret = [] for mux in matches.named('other', lambda match: 'other.validate.mux' in match.tags): source_match = matches.previous(mux, lambda match: match.initiator.name == 'source', 0) if not source_match: ret.append(mux) return ret class ValidateHardcodedSubs(Rule): """Validate HC matches.""" priority = 32 consequence = RemoveMatch def when(self, matches, context): to_remove = [] for hc_match in matches.named('other', predicate=lambda match: match.value == 'Hardcoded Subtitles'): next_match = matches.next(hc_match, predicate=lambda match: match.name == 'subtitle_language', index=0) if next_match and not matches.holes(hc_match.end, next_match.start, predicate=lambda match: match.value.strip(seps)): continue previous_match = matches.previous(hc_match, predicate=lambda match: match.name == 'subtitle_language', index=0) if previous_match and not matches.holes(previous_match.end, hc_match.start, predicate=lambda match: match.value.strip(seps)): continue to_remove.append(hc_match) return to_remove class ValidateStreamingServiceNeighbor(Rule): """Validate streaming service's neighbors.""" priority = 32 consequence = RemoveMatch def when(self, matches, context): to_remove = [] for match in matches.named('other', predicate=lambda m: (m.initiator.name != 'source' and ('streaming_service.prefix' in m.tags or 'streaming_service.suffix' in m.tags))): match = match.initiator if not seps_after(match): if 'streaming_service.prefix' in match.tags: next_match = matches.next(match, lambda m: m.name == 'streaming_service', 0) if next_match and not matches.holes(match.end, next_match.start, predicate=lambda m: m.value.strip(seps)): continue if match.children: to_remove.extend(match.children) to_remove.append(match) elif not seps_before(match): if 'streaming_service.suffix' in match.tags: previous_match = matches.previous(match, lambda m: m.name == 'streaming_service', 0) if previous_match and not matches.holes(previous_match.end, match.start, predicate=lambda m: m.value.strip(seps)): continue if match.children: to_remove.extend(match.children) to_remove.append(match) return to_remove class ValidateAtEnd(Rule): """Validate other which should occur at the end of a filepart.""" priority = 32 consequence = RemoveMatch def when(self, matches, context): to_remove = [] for filepart in matches.markers.named('path'): for match in matches.range(filepart.start, filepart.end, predicate=lambda m: m.name == 'other' and 'at-end' in m.tags): if (matches.holes(match.end, filepart.end, predicate=lambda m: m.value.strip(seps)) or matches.range(match.end, filepart.end, predicate=lambda m: m.name not in ( 'other', 'container'))): to_remove.append(match) return to_remove class ValidateReal(Rule): """ Validate Real """ consequence = RemoveMatch priority = 64 def when(self, matches, context): ret = [] for filepart in matches.markers.named('path'): for match in matches.range(filepart.start, filepart.end, lambda m: m.name == 'other' and 'real' in m.tags): if not matches.range(filepart.start, match.start): ret.append(match) return ret guessit-3.8.0/guessit/rules/properties/part.py000066400000000000000000000025441453655371700215630ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ part property """ from rebulk.remodule import re from rebulk import Rebulk from ..common import dash from ..common.pattern import is_disabled from ..common.validators import seps_surround, int_coercable, and_ from ..common.numeral import numeral, parse_numeral from ...reutils import build_or_pattern def part(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'part')) rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash], validator={'__parent__': seps_surround}) prefixes = config['prefixes'] def validate_roman(match): """ Validate a roman match if surrounded by separators :param match: :type match: :return: :rtype: """ if int_coercable(match.raw): return True return seps_surround(match) rebulk.regex(build_or_pattern(prefixes) + r'-?(?P' + numeral + r')', prefixes=prefixes, validate_all=True, private_parent=True, children=True, formatter=parse_numeral, validator={'part': and_(validate_roman, lambda m: 0 < m.value < 100)}) return rebulk guessit-3.8.0/guessit/rules/properties/release_group.py000066400000000000000000000331671453655371700234560ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ release_group property """ import copy import re from rebulk import Rebulk, Rule, AppendMatch, RemoveMatch from rebulk.match import Match from ..common import seps from ..common.comparators import marker_sorted from ..common.expected import build_expected_function from ..common.formatters import cleanup from ..common.pattern import is_disabled from ..common.validators import int_coercable, seps_surround from ..properties.title import TitleFromPosition def release_group(config): """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ forbidden_groupnames = config['forbidden_names'] groupname_ignore_seps = config['ignored_seps'] groupname_seps = ''.join([c for c in seps if c not in groupname_ignore_seps]) def clean_groupname(string): """ Removes and strip separators from input_string :param string: :type string: :return: :rtype: """ string = string.strip(groupname_seps) if not (string.endswith(tuple(groupname_ignore_seps)) and string.startswith(tuple(groupname_ignore_seps))) \ and not any(i in string.strip(groupname_ignore_seps) for i in groupname_ignore_seps): string = string.strip(groupname_ignore_seps) for forbidden in forbidden_groupnames: if string.lower().startswith(forbidden) and string[len(forbidden):len(forbidden) + 1] in seps: string = string[len(forbidden):] string = string.strip(groupname_seps) if string.lower().endswith(forbidden) and string[-len(forbidden) - 1:-len(forbidden)] in seps: string = string[:len(forbidden)] string = string.strip(groupname_seps) # Release groups that credit individual members often use a format like "Title (MediaInfo Individual) [Group]". # This results in a group name of "Individual) [Group]", which should be transformed to "Individual Group". return re.sub(r'(.+)\)\s?\[(.+)\]', r'\1 \2', string.strip()) rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'release_group')) expected_group = build_expected_function('expected_group') rebulk.functional(expected_group, name='release_group', tags=['expected'], validator=seps_surround, conflict_solver=lambda match, other: other, disabled=lambda context: not context.get('expected_group')) return rebulk.rules( DashSeparatedReleaseGroup(clean_groupname), SceneReleaseGroup(clean_groupname), AnimeReleaseGroup ) _scene_previous_names = ('video_codec', 'source', 'video_api', 'audio_codec', 'audio_profile', 'video_profile', 'audio_channels', 'screen_size', 'other', 'container', 'language', 'subtitle_language', 'subtitle_language.suffix', 'subtitle_language.prefix', 'language.suffix') _scene_previous_tags = ('release-group-prefix',) _scene_no_previous_tags = ('no-release-group-prefix',) class DashSeparatedReleaseGroup(Rule): """ Detect dash separated release groups that might appear at the end or at the beginning of a release name. Series.S01E02.Pilot.DVDRip.x264-CS.mkv release_group: CS abc-the.title.name.1983.1080p.bluray.x264.mkv release_group: abc At the end: Release groups should be dash-separated and shouldn't contain spaces nor appear in a group with other matches. The preceding matches should be separated by dot. If a release group is found, the conflicting matches are removed. At the beginning: Release groups should be dash-separated and shouldn't contain spaces nor appear in a group. It should be followed by a hole with dot-separated words. Detection only happens if no matches exist at the beginning. """ consequence = [RemoveMatch, AppendMatch] def __init__(self, value_formatter): """Default constructor.""" super().__init__() self.value_formatter = value_formatter @classmethod def is_valid(cls, matches, candidate, start, end, at_end): # pylint:disable=inconsistent-return-statements """ Whether a candidate is a valid release group. """ if not at_end: if len(candidate.value) <= 1: return False if matches.markers.at_match(candidate, predicate=lambda m: m.name == 'group'): return False first_hole = matches.holes(candidate.end, end, predicate=lambda m: m.start == candidate.end, index=0) if not first_hole: return False raw_value = first_hole.raw return raw_value[0] == '-' and '-' not in raw_value[1:] and '.' in raw_value and ' ' not in raw_value group = matches.markers.at_match(candidate, predicate=lambda m: m.name == 'group', index=0) if group and matches.at_match(group, predicate=lambda m: not m.private and m.span != candidate.span): return False count = 0 match = candidate while match: current = matches.range(start, match.start, index=-1, predicate=lambda m: not m.private and not 'expected' in m.tags) if not current: break separator = match.input_string[current.end:match.start] if not separator and match.raw[0] == '-': separator = '-' match = current if count == 0: if separator != '-': break count += 1 continue if separator == '.': return True def detect(self, matches, start, end, at_end): # pylint:disable=inconsistent-return-statements """ Detect release group at the end or at the beginning of a filepart. """ candidate = None if at_end: container = matches.ending(end, lambda m: m.name == 'container', index=0) if container: end = container.start candidate = matches.ending(end, index=0, predicate=( lambda m: not m.private and not ( m.name == 'other' and 'not-a-release-group' in m.tags ) and '-' not in m.raw and m.raw.strip() == m.raw)) if not candidate: if at_end: candidate = matches.holes(start, end, seps=seps, index=-1, predicate=lambda m: m.end == end and m.raw.strip(seps) and m.raw[0] == '-') else: candidate = matches.holes(start, end, seps=seps, index=0, predicate=lambda m: m.start == start and m.raw.strip(seps)) if candidate and self.is_valid(matches, candidate, start, end, at_end): return candidate def when(self, matches, context): # pylint:disable=inconsistent-return-statements if matches.named('release_group'): return to_remove = [] to_append = [] for filepart in matches.markers.named('path'): candidate = self.detect(matches, filepart.start, filepart.end, True) if candidate: to_remove.extend(matches.at_match(candidate)) else: candidate = self.detect(matches, filepart.start, filepart.end, False) if candidate: releasegroup = Match(candidate.start, candidate.end, name='release_group', formatter=self.value_formatter, input_string=candidate.input_string) if releasegroup.value: to_append.append(releasegroup) if to_remove or to_append: return to_remove, to_append class SceneReleaseGroup(Rule): """ Add release_group match in existing matches (scene format). Something.XViD-ReleaseGroup.mkv """ dependency = [TitleFromPosition] consequence = AppendMatch properties = {'release_group': [None]} def __init__(self, value_formatter): """Default constructor.""" super().__init__() self.value_formatter = value_formatter @staticmethod def is_previous_match(match): """ Check if match can precede release_group :param match: :return: """ return not match.tagged(*_scene_no_previous_tags) if match.name in _scene_previous_names else \ match.tagged(*_scene_previous_tags) def when(self, matches, context): # pylint:disable=too-many-locals # If a release_group is found before, ignore this kind of release_group rule. ret = [] for filepart in marker_sorted(matches.markers.named('path'), matches): # pylint:disable=cell-var-from-loop start, end = filepart.span if matches.named('release_group', predicate=lambda m: m.start >= start and m.end <= end): continue titles = matches.named('title', predicate=lambda m: m.start >= start and m.end <= end) def keep_only_first_title(match): """ Keep only first title from this filepart, as other ones are most likely release group. :param match: :type match: :return: :rtype: """ return match in titles[1:] last_hole = matches.holes(start, end + 1, formatter=self.value_formatter, ignore=keep_only_first_title, predicate=lambda hole: cleanup(hole.value), index=-1) if last_hole: def previous_match_filter(match): """ Filter to apply to find previous match :param match: :type match: :return: :rtype: """ if match.start < filepart.start: return False return not match.private or self.is_previous_match(match) previous_match = matches.previous(last_hole, previous_match_filter, index=0) if previous_match and (self.is_previous_match(previous_match)) and \ not matches.input_string[previous_match.end:last_hole.start].strip(seps) \ and not int_coercable(last_hole.value.strip(seps)): last_hole.name = 'release_group' last_hole.tags = ['scene'] # if hole is inside a group marker with same value, remove [](){} ... group = matches.markers.at_match(last_hole, lambda marker: marker.name == 'group', 0) if group: group.formatter = self.value_formatter if group.value == last_hole.value: last_hole.start = group.start + 1 last_hole.end = group.end - 1 last_hole.tags = ['anime'] ignored_matches = matches.range(last_hole.start, last_hole.end, keep_only_first_title) for ignored_match in ignored_matches: matches.remove(ignored_match) ret.append(last_hole) return ret class AnimeReleaseGroup(Rule): """ Add release_group match in existing matches (anime format) ...[ReleaseGroup] Something.mkv """ dependency = [SceneReleaseGroup, TitleFromPosition] consequence = [RemoveMatch, AppendMatch] properties = {'release_group': [None]} def when(self, matches, context): to_remove = [] to_append = [] # If a release_group is found before, ignore this kind of release_group rule. if matches.named('release_group'): return False if not matches.named('episode') and not matches.named('season') and matches.named('release_group'): # This doesn't seems to be an anime, and we already found another release_group. return False for filepart in marker_sorted(matches.markers.named('path'), matches): empty_group = matches.markers.range(filepart.start, filepart.end, lambda marker: (marker.name == 'group' and not matches.range(marker.start, marker.end, lambda m: 'weak-language' not in m.tags) and marker.value.strip(seps) and not int_coercable(marker.value.strip(seps))), 0) if empty_group: group = copy.copy(empty_group) group.marker = False group.raw_start += 1 group.raw_end -= 1 group.tags = ['anime'] group.name = 'release_group' to_append.append(group) to_remove.extend(matches.range(empty_group.start, empty_group.end, lambda m: 'weak-language' in m.tags)) if to_remove or to_append: return to_remove, to_append return False guessit-3.8.0/guessit/rules/properties/screen_size.py000066400000000000000000000143161453655371700231260ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ screen_size property """ from rebulk.match import Match from rebulk.remodule import re from rebulk import Rebulk, Rule, RemoveMatch, AppendMatch from ..common.pattern import is_disabled from ..common.quantity import FrameRate from ..common.validators import seps_surround from ..common import dash, seps from ...reutils import build_or_pattern def screen_size(config): """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ interlaced = frozenset(config['interlaced']) progressive = frozenset(config['progressive']) frame_rates = frozenset(config['frame_rates']) min_ar = config['min_ar'] max_ar = config['max_ar'] rebulk = Rebulk() rebulk = rebulk.string_defaults(ignore_case=True).regex_defaults(flags=re.IGNORECASE) rebulk.defaults(name='screen_size', validator=seps_surround, abbreviations=[dash], disabled=lambda context: is_disabled(context, 'screen_size')) frame_rate_pattern = build_or_pattern(frame_rates, name='frame_rate') interlaced_pattern = build_or_pattern(interlaced, name='height') progressive_pattern = build_or_pattern(progressive, name='height') res_pattern = r'(?:(?P\d{3,4})(?:x|\*))?' rebulk.regex(res_pattern + interlaced_pattern + r'(?Pi)' + frame_rate_pattern + '?') rebulk.regex(res_pattern + progressive_pattern + r'(?Pp)' + frame_rate_pattern + '?') rebulk.regex(res_pattern + progressive_pattern + r'(?Pp)?(?:hd)') rebulk.regex(res_pattern + progressive_pattern + r'(?Pp)?x?') rebulk.string('4k', value='2160p', conflict_solver=lambda match, other: '__default__' if other.name == 'screen_size' else match) rebulk.regex(r'(?P\d{3,4})-?(?:x|\*)-?(?P\d{3,4})', conflict_solver=lambda match, other: '__default__' if other.name == 'screen_size' else other) rebulk.regex(frame_rate_pattern + '-?(?:p|fps)', name='frame_rate', formatter=FrameRate.fromstring, disabled=lambda context: is_disabled(context, 'frame_rate')) rebulk.rules(PostProcessScreenSize(progressive, min_ar, max_ar), ScreenSizeOnlyOne, ResolveScreenSizeConflicts) return rebulk class PostProcessScreenSize(Rule): """ Process the screen size calculating the aspect ratio if available. Convert to a standard notation (720p, 1080p, etc) when it's a standard resolution and aspect ratio is valid or not available. It also creates an aspect_ratio match when available. """ consequence = AppendMatch def __init__(self, standard_heights, min_ar, max_ar): super().__init__() self.standard_heights = standard_heights self.min_ar = min_ar self.max_ar = max_ar def when(self, matches, context): to_append = [] for match in matches.named('screen_size'): if not is_disabled(context, 'frame_rate'): for frame_rate in match.children.named('frame_rate'): frame_rate.formatter = FrameRate.fromstring to_append.append(frame_rate) values = match.children.to_dict() if 'height' not in values: continue scan_type = (values.get('scan_type') or 'p').lower() height = values['height'] if 'width' not in values: match.value = f'{height}{scan_type}' continue width = values['width'] calculated_ar = float(width) / float(height) aspect_ratio = Match(match.start, match.end, input_string=match.input_string, name='aspect_ratio', value=round(calculated_ar, 3)) if not is_disabled(context, 'aspect_ratio'): to_append.append(aspect_ratio) if height in self.standard_heights and self.min_ar < calculated_ar < self.max_ar: match.value = f'{height}{scan_type}' else: match.value = f'{width}x{height}' return to_append class ScreenSizeOnlyOne(Rule): """ Keep a single screen_size per filepath part. """ consequence = RemoveMatch def when(self, matches, context): to_remove = [] for filepart in matches.markers.named('path'): screensize = list(reversed(matches.range(filepart.start, filepart.end, lambda match: match.name == 'screen_size'))) if len(screensize) > 1 and len(set((match.value for match in screensize))) > 1: to_remove.extend(screensize[1:]) return to_remove class ResolveScreenSizeConflicts(Rule): """ Resolve screen_size conflicts with season and episode matches. """ consequence = RemoveMatch def when(self, matches, context): to_remove = [] for filepart in matches.markers.named('path'): screensize = matches.range(filepart.start, filepart.end, lambda match: match.name == 'screen_size', 0) if not screensize: continue conflicts = matches.conflicting(screensize, lambda match: match.name in ('season', 'episode')) if not conflicts: continue has_neighbor = False video_profile = matches.range(screensize.end, filepart.end, lambda match: match.name == 'video_profile', 0) if video_profile and not matches.holes(screensize.end, video_profile.start, predicate=lambda h: h.value and h.value.strip(seps)): to_remove.extend(conflicts) has_neighbor = True previous = matches.previous(screensize, index=0, predicate=( lambda m: m.name in ('date', 'source', 'other', 'streaming_service'))) if previous and not matches.holes(previous.end, screensize.start, predicate=lambda h: h.value and h.value.strip(seps)): to_remove.extend(conflicts) has_neighbor = True if not has_neighbor: to_remove.append(screensize) return to_remove guessit-3.8.0/guessit/rules/properties/size.py000066400000000000000000000014711453655371700215650ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ size property """ from rebulk.remodule import re from rebulk import Rebulk from ..common import dash from ..common.quantity import Size from ..common.pattern import is_disabled from ..common.validators import seps_surround def size(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'size')) 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=Size.fromstring, tags=['release-group-prefix']) return rebulk guessit-3.8.0/guessit/rules/properties/source.py000066400000000000000000000260261453655371700221160ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ source property """ import copy from rebulk import AppendMatch, Rebulk, RemoveMatch, Rule from rebulk.remodule import re from .audio_codec import HqConflictRule from ..common import dash, seps, optional from ..common.pattern import is_disabled from ..common.validators import seps_before, seps_after, or_ def source(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'source')) rebulk = rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash], private_parent=True, children=True) rebulk = rebulk.defaults(name='source', tags=['video-codec-prefix', 'streaming_service.suffix'], validate_all=True, validator={'__parent__': or_(seps_before, seps_after)}) rip_prefix = config['rip_prefix'] rip_suffix = config['rip_suffix'] def build_source_pattern(*patterns, prefix='', suffix=''): """Helper pattern to build source pattern.""" return [prefix + f'({pattern})' + suffix for pattern in patterns] def demote_other(match, other): # pylint: disable=unused-argument """Default conflict solver with 'other' property.""" return other if other.name in ['other', 'release_group'] else '__default__' rebulk.regex(*build_source_pattern('VHS', suffix=optional(rip_suffix)), value={'source': 'VHS', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('CAM', suffix=optional(rip_suffix)), value={'source': 'Camera', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('HD-?CAM', suffix=optional(rip_suffix)), value={'source': 'HD Camera', 'other': 'Rip'}) # For TS, we remove 'streaming_service.suffix' tag to avoid "Shots" being guessed as Showtime and TS. rebulk.regex(*build_source_pattern('TELESYNC', 'TS', suffix=optional(rip_suffix)), value={'source': 'Telesync', 'other': 'Rip'}, tags=['video-codec-prefix'], overrides=["tags"]) rebulk.regex(*build_source_pattern('HD-?TELESYNC', 'HD-?TS', suffix=optional(rip_suffix)), value={'source': 'HD Telesync', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('WORKPRINT', 'WP'), value='Workprint') rebulk.regex(*build_source_pattern('TELECINE', 'TC', suffix=optional(rip_suffix)), value={'source': 'Telecine', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('HD-?TELECINE', 'HD-?TC', suffix=optional(rip_suffix)), value={'source': 'HD Telecine', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('PPV', suffix=optional(rip_suffix)), value={'source': 'Pay-per-view', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('SD-?TV', suffix=optional(rip_suffix)), value={'source': 'TV', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('TV', suffix=rip_suffix), # TV is too common to allow matching value={'source': 'TV', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('TV', 'SD-?TV', prefix=rip_prefix), value={'source': 'TV', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('TV-?(?=Dub)'), value='TV') rebulk.regex(*build_source_pattern('DVB', 'PD-?TV', suffix=optional(rip_suffix)), value={'source': 'Digital TV', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('DVD', suffix=optional(rip_suffix)), value={'source': 'DVD', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('DM', suffix=optional(rip_suffix)), value={'source': 'Digital Master', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('VIDEO-?TS', 'DVD-?R(?:$|(?!E))', # 'DVD-?R(?:$|^E)' => DVD-Real ... 'DVD-?9', 'DVD-?5'), value='DVD') rebulk.regex(*build_source_pattern('HD-?TV', suffix=optional(rip_suffix)), conflict_solver=demote_other, value={'source': 'HDTV', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('TV-?HD', suffix=rip_suffix), conflict_solver=demote_other, value={'source': 'HDTV', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('TV', suffix='-?(?PRip-?HD)'), conflict_solver=demote_other, value={'source': 'HDTV', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('VOD', suffix=optional(rip_suffix)), value={'source': 'Video on Demand', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('WEB', 'WEB-?DL', suffix=rip_suffix), value={'source': 'Web', 'other': 'Rip'}) # WEBCap is a synonym to WEBRip, mostly used by non english rebulk.regex(*build_source_pattern('WEB-?(?PCap)', suffix=optional(rip_suffix)), value={'source': 'Web', 'other': 'Rip', 'another': 'Rip'}) rebulk.regex(*build_source_pattern('WEB-?DL', 'WEB-?U?HD', 'DL-?WEB', 'DL(?=-?Mux)'), value={'source': 'Web'}) rebulk.regex('(WEB)', value='Web', tags='weak.source') rebulk.regex(*build_source_pattern('HD-?DVD', suffix=optional(rip_suffix)), value={'source': 'HD-DVD', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('Blu-?ray', 'BD', 'BD[59]', 'BD25', 'BD50', suffix=optional(rip_suffix)), value={'source': 'Blu-ray', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('(?PBR)-?(?=Scr(?:eener)?)', '(?PBR)-?(?=Mux)'), # BRRip value={'source': 'Blu-ray', 'another': 'Reencoded'}) rebulk.regex(*build_source_pattern('(?PBR)', suffix=rip_suffix), # BRRip value={'source': 'Blu-ray', 'other': 'Rip', 'another': 'Reencoded'}) rebulk.regex(*build_source_pattern('Ultra-?Blu-?ray', 'Blu-?ray-?Ultra'), value='Ultra HD Blu-ray') rebulk.regex(*build_source_pattern('AHDTV'), value='Analog HDTV') rebulk.regex(*build_source_pattern('UHD-?TV', suffix=optional(rip_suffix)), conflict_solver=demote_other, value={'source': 'Ultra HDTV', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('UHD', suffix=rip_suffix), conflict_solver=demote_other, value={'source': 'Ultra HDTV', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('DSR', 'DTH', suffix=optional(rip_suffix)), value={'source': 'Satellite', 'other': 'Rip'}) rebulk.regex(*build_source_pattern('DSR?', 'SAT', suffix=rip_suffix), value={'source': 'Satellite', 'other': 'Rip'}) rebulk.rules(ValidateSourcePrefixSuffix, ValidateWeakSource, UltraHdBlurayRule) return rebulk class UltraHdBlurayRule(Rule): """ Replace other:Ultra HD and source:Blu-ray with source:Ultra HD Blu-ray """ dependency = HqConflictRule consequence = [RemoveMatch, AppendMatch] @classmethod def find_ultrahd(cls, matches, start, end, index): """Find Ultra HD match.""" return matches.range(start, end, index=index, predicate=( lambda m: not m.private and m.name == 'other' and m.value == 'Ultra HD' )) @classmethod def validate_range(cls, matches, start, end): """Validate no holes or invalid matches exist in the specified range.""" return ( not matches.holes(start, end, predicate=lambda m: m.value.strip(seps)) and not matches.range(start, end, predicate=( lambda m: not m.private and ( m.name not in ('screen_size', 'color_depth') and ( m.name != 'other' or 'uhdbluray-neighbor' not in m.tags)))) ) def when(self, matches, context): to_remove = [] to_append = [] for filepart in matches.markers.named('path'): for match in matches.range(filepart.start, filepart.end, predicate=( lambda m: not m.private and m.name == 'source' and m.value == 'Blu-ray')): other = self.find_ultrahd(matches, filepart.start, match.start, -1) if not other or not self.validate_range(matches, other.end, match.start): other = self.find_ultrahd(matches, match.end, filepart.end, 0) if not other or not self.validate_range(matches, match.end, other.start): if not matches.range(filepart.start, filepart.end, predicate=( lambda m: m.name == 'screen_size' and m.value == '2160p')): continue if other: other.private = True new_source = copy.copy(match) new_source.value = 'Ultra HD Blu-ray' to_remove.append(match) to_append.append(new_source) if to_remove or to_append: return to_remove, to_append return False class ValidateSourcePrefixSuffix(Rule): """ Validate source with source prefix, source suffix. """ priority = 64 consequence = RemoveMatch def when(self, matches, context): ret = [] for filepart in matches.markers.named('path'): for match in matches.range(filepart.start, filepart.end, predicate=lambda m: m.name == 'source'): match = match.initiator if not seps_before(match) and \ not matches.range(match.start - 1, match.start - 2, lambda m: 'source-prefix' in m.tags): if match.children: ret.extend(match.children) ret.append(match) continue if not seps_after(match) and \ not matches.range(match.end, match.end + 1, lambda m: 'source-suffix' in m.tags): if match.children: ret.extend(match.children) ret.append(match) continue return ret class ValidateWeakSource(Rule): """ Validate weak source """ dependency = [ValidateSourcePrefixSuffix] priority = 64 consequence = RemoveMatch def when(self, matches, context): ret = [] for filepart in matches.markers.named('path'): for match in matches.range(filepart.start, filepart.end, predicate=lambda m: m.name == 'source'): # if there are more than 1 source in this filepart, just before the year and with holes for the title # most likely the source is part of the title if 'weak.source' in match.tags \ and matches.range(match.end, filepart.end, predicate=lambda m: m.name == 'source') \ and matches.holes(filepart.start, match.start, predicate=lambda m: m.value.strip(seps), index=-1): if match.children: ret.extend(match.children) ret.append(match) continue return ret guessit-3.8.0/guessit/rules/properties/streaming_service.py000066400000000000000000000051601453655371700243230ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ streaming_service property """ from rebulk.remodule import re from rebulk import Rebulk from rebulk.rules import Rule, RemoveMatch from ..common.pattern import is_disabled from ...config import load_config_patterns from ...rules.common import seps, dash from ...rules.common.validators import seps_before, seps_after def streaming_service(config): # pylint: disable=too-many-statements,unused-argument """Streaming service property. :param config: rule configuration :type config: dict :return: :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'streaming_service')) rebulk = rebulk.string_defaults(ignore_case=True).regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]) rebulk.defaults(name='streaming_service', tags=['source-prefix']) load_config_patterns(rebulk, config) rebulk.rules(ValidateStreamingService) return rebulk class ValidateStreamingService(Rule): """Validate streaming service matches.""" priority = 128 consequence = RemoveMatch def when(self, matches, context): """Streaming service is always before source. :param matches: :type matches: rebulk.match.Matches :param context: :type context: dict :return: """ to_remove = [] for service in matches.named('streaming_service'): next_match = matches.next(service, lambda match: 'streaming_service.suffix' in match.tags, 0) previous_match = matches.previous(service, lambda match: 'streaming_service.prefix' in match.tags, 0) has_other = service.initiator and service.initiator.children.named('other') if not has_other: if (not next_match or matches.holes(service.end, next_match.start, predicate=lambda match: match.value.strip(seps)) or not seps_before(service)): if (not previous_match or matches.holes(previous_match.end, service.start, predicate=lambda match: match.value.strip(seps)) or not seps_after(service)): to_remove.append(service) continue if service.value == 'Comedy Central': # Current match is a valid streaming service, removing invalid Criterion Collection (CC) matches to_remove.extend(matches.named('edition', predicate=lambda match: match.value == 'Criterion')) return to_remove guessit-3.8.0/guessit/rules/properties/title.py000066400000000000000000000362171453655371700217420ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ title property """ from rebulk import Rebulk, Rule, AppendMatch, RemoveMatch, AppendTags from rebulk.formatters import formatters from .film import FilmTitleRule from .language import ( SubtitlePrefixLanguageRule, SubtitleSuffixLanguageRule, SubtitleExtensionRule, NON_SPECIFIC_LANGUAGES ) from ..common import seps, title_seps from ..common.comparators import marker_sorted from ..common.expected import build_expected_function from ..common.formatters import cleanup, reorder_title from ..common.pattern import is_disabled from ..common.validators import seps_surround def title(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'title')) rebulk.rules(TitleFromPosition, PreferTitleWithYear) expected_title = build_expected_function('expected_title') rebulk.functional(expected_title, name='title', tags=['expected', 'title'], validator=seps_surround, formatter=formatters(cleanup, reorder_title), conflict_solver=lambda match, other: other, disabled=lambda context: not context.get('expected_title')) return rebulk class TitleBaseRule(Rule): """ Add title match in existing matches """ # pylint:disable=unused-argument consequence = [AppendMatch, RemoveMatch] def __init__(self, match_name, match_tags=None, alternative_match_name=None): super().__init__() self.match_name = match_name self.match_tags = match_tags self.alternative_match_name = alternative_match_name def hole_filter(self, hole, matches): """ Filter holes for titles. :param hole: :type hole: :param matches: :type matches: :return: :rtype: """ return True def filepart_filter(self, filepart, matches): """ Filter filepart for titles. :param filepart: :type filepart: :param matches: :type matches: :return: :rtype: """ return True def holes_process(self, holes, matches): """ process holes :param holes: :type holes: :param matches: :type matches: :return: :rtype: """ cropped_holes = [] group_markers = matches.markers.named('group') for group_marker in group_markers: path_marker = matches.markers.at_match(group_marker, predicate=lambda m: m.name == 'path', index=0) if path_marker and path_marker.span == group_marker.span: group_markers.remove(group_marker) for hole in holes: cropped_holes.extend(hole.crop(group_markers)) return cropped_holes @staticmethod def is_ignored(match): """ Ignore matches when scanning for title (hole). Full word language and countries won't be ignored if they are uppercase. """ return not (len(match) > 3 and match.raw.isupper()) and match.name in ('language', 'country', 'episode_details') def should_keep(self, match, to_keep, matches, filepart, hole, starting): """ Check if this match should be accepted when ending or starting a hole. :param match: :type match: :param to_keep: :type to_keep: list[Match] :param matches: :type matches: Matches :param hole: the filepart match :type hole: Match :param hole: the hole match :type hole: Match :param starting: true if match is starting the hole :type starting: bool :return: :rtype: """ if match.name in ('language', 'country'): # Keep language if exactly matching the hole. if len(hole.value) == len(match.raw): return True # Keep language if other languages exists in the filepart. outside_matches = filepart.crop(hole) other_languages = [] for outside in outside_matches: other_languages.extend(matches.range(outside.start, outside.end, lambda c_match: c_match.name == match.name and c_match not in to_keep and c_match.value not in NON_SPECIFIC_LANGUAGES)) if not other_languages and (not starting or len(match.raw) <= 3): return True return False def should_remove(self, match, matches, filepart, hole, context): """ Check if this match should be removed after beeing ignored. :param match: :param matches: :param filepart: :param hole: :return: """ if context.get('type') == 'episode' and match.name == 'episode_details': return match.start >= hole.start and match.end <= hole.end return True def check_titles_in_filepart(self, filepart, matches, context, # pylint:disable=inconsistent-return-statements additional_ignored=None): """ Find title in filepart (ignoring language) """ # pylint:disable=too-many-locals,too-many-branches,too-many-statements start, end = filepart.span holes = matches.holes(start, end + 1, formatter=formatters(cleanup, reorder_title), ignore=self.is_ignored if additional_ignored is None else lambda m: self.is_ignored( m) or additional_ignored(m), predicate=lambda m: m.value) holes = self.holes_process(holes, matches) for hole in holes: if not hole or (self.hole_filter and not self.hole_filter(hole, matches)): continue to_remove = [] to_keep = [] ignored_matches = matches.range(hole.start, hole.end, self.is_ignored) if ignored_matches: for ignored_match in reversed(ignored_matches): # pylint:disable=undefined-loop-variable, cell-var-from-loop trailing = matches.chain_before(hole.end, seps, predicate=lambda m: m == ignored_match) if trailing: should_keep = self.should_keep(ignored_match, to_keep, matches, filepart, hole, False) if should_keep: # pylint:disable=unpacking-non-sequence try: append, crop = should_keep except TypeError: append, crop = should_keep, should_keep if append: to_keep.append(ignored_match) if crop: hole.end = ignored_match.start for ignored_match in ignored_matches: if ignored_match not in to_keep: starting = matches.chain_after(hole.start, seps, predicate=lambda m, im=ignored_match: m == im) if starting: should_keep = self.should_keep(ignored_match, to_keep, matches, filepart, hole, True) if should_keep: # pylint:disable=unpacking-non-sequence try: append, crop = should_keep except TypeError: append, crop = should_keep, should_keep if append: to_keep.append(ignored_match) if crop: hole.start = ignored_match.end for match in ignored_matches: if self.should_remove(match, matches, filepart, hole, context): to_remove.append(match) for keep_match in to_keep: if keep_match in to_remove: to_remove.remove(keep_match) if hole and hole.value: hole.name = self.match_name hole.tags = self.match_tags if self.alternative_match_name: # Split and keep values that can be a title titles = hole.split(title_seps, lambda m: m.value) for title_match in list(titles[1:]): previous_title = titles[titles.index(title_match) - 1] separator = matches.input_string[previous_title.end:title_match.start] if len(separator) == 1 and separator == '-' \ and previous_title.raw[-1] not in seps \ and title_match.raw[0] not in seps: titles[titles.index(title_match) - 1].end = title_match.end titles.remove(title_match) else: title_match.name = self.alternative_match_name else: titles = [hole] return titles, to_remove def _serie_name_filepart(self, matches, fileparts): # Try to get show title from subdirectory of a season only directory (Show Name/Season 1/episode_title.avi) for index in range(len(fileparts) - 1): if index == 0: continue filepart = fileparts[index] filepart_matches = [m for m in matches.range(filepart.start, filepart.end) if not m.private] if len(filepart_matches) == 1 and filepart_matches[0].name == 'season' and \ (filepart_matches[0].span == filepart.span or filepart_matches[0].parent and filepart_matches[0].parent.span == filepart.span): # Filepath match season match exactly return fileparts[index + 1] return None def _serie_name_filepart_match(self, matches, context, serie_name_filepart, to_append, to_remove): def serie_name_filepart_ignored(match): for tag in match.tags: if tag == 'weak' or tag.startswith('weak-'): return True return False titles = self.check_titles_in_filepart(serie_name_filepart, matches, context, serie_name_filepart_ignored) if titles: titles, to_remove_c = titles if len(titles) == 1: to_append.extend(titles) to_remove.extend(to_remove_c) return titles[0] return None def _year_fileparts(self, matches, fileparts): year_fileparts = [] for filepart in fileparts: year_match = matches.range(filepart.start, filepart.end, lambda match: match.name == 'year', 0) if year_match: year_fileparts.append(filepart) return year_fileparts def when(self, matches, context): to_append = [] to_remove = [] if matches.named(self.match_name, lambda match: 'expected' in match.tags): return False fileparts = [filepart for filepart in list(marker_sorted(matches.markers.named('path'), matches)) if not self.filepart_filter or self.filepart_filter(filepart, matches)] serie_name_filepart = self._serie_name_filepart(matches, fileparts) serie_name_filepath_match = None if serie_name_filepart: serie_name_filepath_match = self._serie_name_filepart_match(matches, context, serie_name_filepart, to_append, to_remove) # Force inclusion of fileparts containing the year year_fileparts = self._year_fileparts(matches, fileparts) for filepart in fileparts: try: year_fileparts.remove(filepart) except ValueError: pass titles = self.check_titles_in_filepart(filepart, matches, context) if titles: titles, to_remove_c = titles if serie_name_filepath_match: for title_match in titles: if title_match.value != serie_name_filepath_match.value: title_match.name = 'episode_title' to_append.extend(titles) to_remove.extend(to_remove_c) break # Add title match in all fileparts containing the year. for filepart in year_fileparts: titles = self.check_titles_in_filepart(filepart, matches, context) if titles: # pylint:disable=unbalanced-tuple-unpacking titles, to_remove_c = titles to_append.extend(titles) to_remove.extend(to_remove_c) if to_append or to_remove: return to_append, to_remove return False class TitleFromPosition(TitleBaseRule): """ Add title match in existing matches """ dependency = [FilmTitleRule, SubtitlePrefixLanguageRule, SubtitleSuffixLanguageRule, SubtitleExtensionRule] properties = {'title': [None], 'alternative_title': [None]} def __init__(self): super().__init__('title', ['title'], 'alternative_title') def enabled(self, context): return not is_disabled(context, 'alternative_title') class PreferTitleWithYear(Rule): """ Prefer title where filepart contains year. """ dependency = TitleFromPosition consequence = [RemoveMatch, AppendTags(['equivalent-ignore'])] properties = {'title': [None]} def when(self, matches, context): with_year_in_group = [] with_year = [] titles = matches.named('title') for title_match in titles: filepart = matches.markers.at_match(title_match, lambda marker: marker.name == 'path', 0) if filepart: year_match = matches.range(filepart.start, filepart.end, lambda match: match.name == 'year', 0) if year_match: group = matches.markers.at_match(year_match, lambda m: m.name == 'group') if group: with_year_in_group.append(title_match) else: with_year.append(title_match) to_tag = [] if with_year_in_group: title_values = {title_match.value for title_match in with_year_in_group} to_tag.extend(with_year_in_group) elif with_year: title_values = {title_match.value for title_match in with_year} to_tag.extend(with_year) else: title_values = {title_match.value for title_match in titles} to_remove = [] for title_match in titles: if title_match.value not in title_values: to_remove.append(title_match) if to_remove or to_tag: return to_remove, to_tag return False guessit-3.8.0/guessit/rules/properties/type.py000066400000000000000000000042211453655371700215700ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ type property """ from rebulk import CustomRule, Rebulk, POST_PROCESS from rebulk.match import Match from ..common.pattern import is_disabled from ...rules.processors import Processors def _type(matches, value): """ Define type match with given value. :param matches: :param value: :return: """ matches.append(Match(len(matches.input_string), len(matches.input_string), name='type', value=value)) def type_(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'type')) rebulk = rebulk.rules(TypeProcessor) return rebulk class TypeProcessor(CustomRule): """ Post processor to find file type based on all others found matches. """ priority = POST_PROCESS dependency = Processors properties = {'type': ['episode', 'movie']} def when(self, matches, context): # pylint:disable=too-many-return-statements option_type = context.get('type', None) if option_type: return option_type episode = matches.named('episode') season = matches.named('season') absolute_episode = matches.named('absolute_episode') episode_details = matches.named('episode_details') if episode or season or episode_details or absolute_episode: return 'episode' film = matches.named('film') if film: return 'movie' year = matches.named('year') date = matches.named('date') if date and not year: return 'episode' bonus = matches.named('bonus') if bonus and not year: return 'episode' crc32 = matches.named('crc32') anime_release_group = matches.named('release_group', lambda match: 'anime' in match.tags) if crc32 and anime_release_group: return 'episode' return 'movie' def then(self, matches, when_response, context): _type(matches, when_response) guessit-3.8.0/guessit/rules/properties/video_codec.py000066400000000000000000000115371453655371700230620ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ video_codec and video_profile property """ from rebulk import Rebulk, Rule, RemoveMatch from rebulk.remodule import re from ..common import dash from ..common.pattern import is_disabled from ..common.validators import seps_after, seps_before, seps_surround def video_codec(config): # pylint:disable=unused-argument """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk() rebulk = rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]).string_defaults(ignore_case=True) rebulk.defaults(name="video_codec", tags=['source-suffix', 'streaming_service.suffix'], disabled=lambda context: is_disabled(context, 'video_codec')) rebulk.regex(r'Rv\d{2}', value='RealVideo') rebulk.regex('Mpe?g-?2', '[hx]-?262', value='MPEG-2') rebulk.string("DVDivX", "DivX", value="DivX") rebulk.string('XviD', value='Xvid') rebulk.regex('VC-?1', value='VC-1') rebulk.string('VP7', value='VP7') rebulk.string('VP8', 'VP80', value='VP8') rebulk.string('VP9', value='VP9') rebulk.regex('[hx]-?263', value='H.263') rebulk.regex('[hx]-?264', '(MPEG-?4)?AVC(?:HD)?', value='H.264') rebulk.regex('[hx]-?265', 'HEVC', value='H.265') rebulk.regex('(?Phevc)(?P10)', value={'video_codec': 'H.265', 'color_depth': '10-bit'}, tags=['video-codec-suffix'], children=True) # http://blog.mediacoderhq.com/h264-profiles-and-levels/ # https://en.wikipedia.org/wiki/H.264/MPEG-4_AVC rebulk.defaults(clear=True, name="video_profile", validator=seps_surround, disabled=lambda context: is_disabled(context, 'video_profile')) rebulk.string('BP', value='Baseline', tags='video_profile.rule') rebulk.string('XP', 'EP', value='Extended', tags='video_profile.rule') rebulk.string('MP', value='Main', tags='video_profile.rule') rebulk.string('HP', 'HiP', value='High', tags='video_profile.rule') # https://en.wikipedia.org/wiki/Scalable_Video_Coding rebulk.string('SC', 'SVC', value='Scalable Video Coding', tags='video_profile.rule') # https://en.wikipedia.org/wiki/AVCHD rebulk.regex('AVC(?:HD)?', value='Advanced Video Codec High Definition', tags='video_profile.rule') # https://en.wikipedia.org/wiki/H.265/HEVC rebulk.string('HEVC', value='High Efficiency Video Coding', tags='video_profile.rule') rebulk.regex('Hi422P', value='High 4:2:2') rebulk.regex('Hi444PP', value='High 4:4:4 Predictive') rebulk.regex('Hi10P?', value='High 10') # no profile validation is required rebulk.string('DXVA', value='DXVA', name='video_api', disabled=lambda context: is_disabled(context, 'video_api')) rebulk.defaults(clear=True, name='color_depth', validator=seps_surround, disabled=lambda context: is_disabled(context, 'color_depth')) rebulk.regex('12.?bits?', value='12-bit') rebulk.regex('10.?bits?', 'YUV420P10', 'Hi10P?', value='10-bit') rebulk.regex('8.?bits?', value='8-bit') rebulk.rules(ValidateVideoCodec, VideoProfileRule) return rebulk class ValidateVideoCodec(Rule): """ Validate video_codec with source property or separated """ priority = 64 consequence = RemoveMatch def enabled(self, context): return not is_disabled(context, 'video_codec') def when(self, matches, context): ret = [] for codec in matches.named('video_codec'): if not seps_before(codec) and \ not matches.at_index(codec.start - 1, lambda match: 'video-codec-prefix' in match.tags): ret.append(codec) continue if not seps_after(codec) and \ not matches.at_index(codec.end + 1, lambda match: 'video-codec-suffix' in match.tags): ret.append(codec) continue return ret class VideoProfileRule(Rule): """ Rule to validate video_profile """ consequence = RemoveMatch def enabled(self, context): return not is_disabled(context, 'video_profile') def when(self, matches, context): profile_list = matches.named('video_profile', lambda match: 'video_profile.rule' in match.tags) ret = [] for profile in profile_list: codec = matches.at_span(profile.span, lambda match: match.name == 'video_codec', 0) if not codec: codec = matches.previous(profile, lambda match: match.name == 'video_codec') if not codec: codec = matches.next(profile, lambda match: match.name == 'video_codec') if not codec: ret.append(profile) return ret guessit-3.8.0/guessit/rules/properties/website.py000066400000000000000000000101741453655371700222550ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Website property. """ try: from importlib.resources import files # @UnresolvedImport except ImportError: from importlib_resources import files # @UnresolvedImport from rebulk.remodule import re from rebulk import Rebulk, Rule, RemoveMatch from ..common import seps from ..common.formatters import cleanup from ..common.pattern import is_disabled from ..common.validators import seps_surround from ...reutils import build_or_pattern def website(config): """ Builder for rebulk object. :param config: rule configuration :type config: dict :return: Created Rebulk object :rtype: Rebulk """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'website')) rebulk = rebulk.regex_defaults(flags=re.IGNORECASE).string_defaults(ignore_case=True) rebulk.defaults(name="website") data_files = files('guessit.data') tld_file = data_files.joinpath('tlds-alpha-by-domain.txt').read_text(encoding='utf-8') tlds = [ tld.strip() for tld in tld_file.split('\n') if '--' not in tld ][1:] # All registered domain extension safe_tlds = config['safe_tlds'] # For sure a website extension safe_subdomains = config['safe_subdomains'] # For sure a website subdomain safe_prefix = config['safe_prefixes'] # Those words before a tlds are sure website_prefixes = config['prefixes'] rebulk.regex(r'(?:[^a-z0-9]|^)((?:'+build_or_pattern(safe_subdomains) + r'\.)+(?:[a-z-0-9-]+\.)+(?:'+build_or_pattern(tlds) + r'))(?:[^a-z0-9]|$)', children=True) rebulk.regex(r'(?:[^a-z0-9]|^)((?:'+build_or_pattern(safe_subdomains) + r'\.)*[a-z0-9-]+\.(?:'+build_or_pattern(safe_tlds) + r'))(?:[^a-z0-9]|$)', safe_subdomains=safe_subdomains, safe_tlds=safe_tlds, children=True) rebulk.regex(r'(?:[^a-z0-9]|^)((?:'+build_or_pattern(safe_subdomains) + r'\.)*[a-z0-9-]+\.(?:'+build_or_pattern(safe_prefix) + r'\.)+(?:'+build_or_pattern(tlds) + r'))(?:[^a-z0-9]|$)', safe_subdomains=safe_subdomains, safe_prefix=safe_prefix, tlds=tlds, children=True) rebulk.string(*website_prefixes, validator=seps_surround, private=True, tags=['website.prefix']) class PreferTitleOverWebsite(Rule): """ If found match is more likely a title, remove website. """ consequence = RemoveMatch @staticmethod def valid_followers(match): """ Validator for next website matches """ return match.named('season', 'episode', 'year') def when(self, matches, context): to_remove = [] for website_match in matches.named('website'): safe = False for safe_start in safe_subdomains + safe_prefix: if website_match.value.lower().startswith(safe_start): safe = True break if not safe: suffix = matches.next(website_match, PreferTitleOverWebsite.valid_followers, 0) if suffix: group = matches.markers.at_match(website_match, lambda marker: marker.name == 'group', 0) if not group: to_remove.append(website_match) return to_remove rebulk.rules(PreferTitleOverWebsite, ValidateWebsitePrefix) return rebulk class ValidateWebsitePrefix(Rule): """ Validate website prefixes """ priority = 64 consequence = RemoveMatch def when(self, matches, context): to_remove = [] for prefix in matches.tagged('website.prefix'): website_match = matches.next(prefix, predicate=lambda match: match.name == 'website', index=0) if (not website_match or matches.holes(prefix.end, website_match.start, formatter=cleanup, seps=seps, predicate=lambda match: match.value)): to_remove.append(prefix) return to_remove guessit-3.8.0/guessit/test/000077500000000000000000000000001453655371700156675ustar00rootroot00000000000000guessit-3.8.0/guessit/test/__init__.py000066400000000000000000000001651453655371700200020ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=pointless-statement, missing-docstring, invalid-name guessit-3.8.0/guessit/test/config/000077500000000000000000000000001453655371700171345ustar00rootroot00000000000000guessit-3.8.0/guessit/test/config/dummy.txt000066400000000000000000000000301453655371700210210ustar00rootroot00000000000000Not a configuration fileguessit-3.8.0/guessit/test/config/test.json000066400000000000000000000001001453655371700207750ustar00rootroot00000000000000{ "expected_title": ["The 100", "OSS 117"], "yaml": false } guessit-3.8.0/guessit/test/config/test.yaml000066400000000000000000000000631453655371700207760ustar00rootroot00000000000000expected_title: - The 100 - OSS 117 yaml: True guessit-3.8.0/guessit/test/config/test.yml000066400000000000000000000000631453655371700206350ustar00rootroot00000000000000expected_title: - The 100 - OSS 117 yaml: True guessit-3.8.0/guessit/test/enable_disable_properties.yml000066400000000000000000000140141453655371700235770ustar00rootroot00000000000000? vorbis : options: --exclude audio_codec -audio_codec: Vorbis ? DTS-ES : options: --exclude audio_profile audio_codec: DTS -audio_profile: Extended Surround ? DTS.ES : options: --include audio_codec audio_codec: DTS -audio_profile: Extended Surround ? 5.1 ? 5ch ? 6ch : options: --exclude audio_channels -audio_channels: '5.1' ? Movie Title-x01-Other Title.mkv ? Movie Title-x01-Other Title ? directory/Movie Title-x01-Other Title/file.mkv : options: --exclude bonus -bonus: 1 -bonus_title: Other Title ? Title-x02-Bonus Title.mkv : options: --include bonus bonus: 2 -bonus_title: Other Title ? cd 1of3 : options: --exclude cd -cd: 1 -cd_count: 3 ? This.is.Us : options: --exclude country title: This is Us -country: US ? 2015.01.31 : options: --exclude date year: 2015 -date: 2015-01-31 ? Something 2 mar 2013) : options: --exclude date -date: 2013-03-02 ? 2012 2009 S01E02 2015 # If no year is marked, the second one is guessed. : options: --exclude year -year: 2009 ? Director's cut : options: --exclude edition -edition: Director's Cut ? 2x5 ? 2X5 ? 02x05 ? 2X05 ? 02x5 ? S02E05 ? s02e05 ? s02e5 ? s2e05 ? s02ep05 ? s2EP5 : options: --exclude season -season: 2 -episode: 5 ? 2x6 ? 2X6 ? 02x06 ? 2X06 ? 02x6 ? S02E06 ? s02e06 ? s02e6 ? s2e06 ? s02ep06 ? s2EP6 : options: --exclude episode -season: 2 -episode: 6 ? serie Season 2 other : options: --exclude season -season: 2 ? Some Dummy Directory/S02 Some Series/E01-Episode title.mkv : options: --exclude episode_title -episode_title: Episode title season: 2 episode: 1 ? Another Dummy Directory/S02 Some Series/E01-Episode title.mkv : options: --include season --include episode -episode_title: Episode title season: 2 episode: 1 # pattern contains season and episode: it wont work enabling only one ? Some Series S03E01E02 : options: --include episode -season: 3 -episode: [1, 2] # pattern contains season and episode: it wont work enabling only one ? Another Series S04E01E02 : options: --include season -season: 4 -episode: [1, 2] ? Show.Name.Season.4.Episode.1 : options: --include episode -season: 4 episode: 1 ? Another.Show.Name.Season.4.Episode.1 : options: --include season season: 4 -episode: 1 ? Some Series S01 02 03 : options: --exclude season -season: [1, 2, 3] ? Some Series E01 02 04 : options: --exclude episode -episode: [1, 2, 4] ? A very special episode s06 special : options: -t episode --exclude episode_details season: 6 -episode_details: Special ? S01D02.3-5-GROUP : options: --exclude disc -season: 1 -disc: [2, 3, 4, 5] -episode: [2, 3, 4, 5] ? S01D02&4-6&8 : options: --exclude season -season: 1 -disc: [2, 4, 5, 6, 8] -episode: [2, 4, 5, 6, 8] ? Film Title-f01-Series Title.mkv : options: --exclude film -film: 1 -film_title: Film Title ? Another Film Title-f01-Series Title.mkv : options: --exclude film_title film: 1 -film_title: Film Title ? English ? .ENG. : options: --exclude language -language: English ? SubFrench ? SubFr ? STFr : options: --exclude subtitle_language -language: French -subtitle_language: French ? ST.FR : options: --exclude subtitle_language language: French -subtitle_language: French ? ENG.-.sub.FR ? ENG.-.FR Sub : options: --include language language: [English, French] -subtitle_language: French ? ENG.-.SubFR : options: --include language language: English -subtitle_language: French ? ENG.-.FRSUB ? ENG.-.FRSUBS ? ENG.-.FR-SUBS : options: --include subtitle_language -language: English subtitle_language: French ? DVD.Real.XViD ? DVD.fix.XViD : options: --exclude other -other: Fix -proper_count: 1 ? Part 3 ? Part III ? Part Three ? Part Trois ? Part3 : options: --exclude part -part: 3 ? Some.Title.XViD-by.Artik[SEDG].avi : options: --exclude release_group -release_group: Artik[SEDG] ? "[ABC] Some.Title.avi" ? some/folder/[ABC]Some.Title.avi : options: --exclude release_group -release_group: ABC ? 360p ? 360px ? "360" ? +500x360 : options: --exclude screen_size -screen_size: 360p ? 640x360 : options: --exclude aspect_ratio screen_size: 360p -aspect_ratio: 1.778 ? 8196x4320 : options: --exclude screen_size -screen_size: 4320p -aspect_ratio: 1.897 ? 4.3gb : options: --exclude size -size: 4.3GB ? VhS_rip ? VHS.RIP : options: --exclude source -source: VHS -other: Rip ? DVD.RIP : options: --include other -source: DVD -other: Rip ? Title Only.avi : options: --exclude title -title: Title Only ? h265 ? x265 ? h.265 ? x.265 ? hevc : options: --exclude video_codec -video_codec: H.265 ? hevc10 : options: --include color_depth -video_codec: H.265 -color_depth: 10-bit ? HEVC-YUV420P10 : options: --include color_depth -video_codec: H.265 color_depth: 10-bit ? h265-HP : options: --exclude video_profile video_codec: H.265 -video_profile: High ? House.of.Cards.2013.S02E03.1080p.NF.WEBRip.DD5.1.x264-NTb.mkv ? House.of.Cards.2013.S02E03.1080p.Netflix.WEBRip.DD5.1.x264-NTb.mkv : options: --exclude streaming_service -streaming_service: Netflix ? wawa.co.uk : options: --exclude website -website: wawa.co.uk ? movie.mp4 : options: --exclude mimetype -mimetype: video/mp4 ? another movie.mkv : options: --exclude container -container: mkv ? series s02e01 : options: --exclude type -type: episode ? series s02e01 : options: --exclude type -type: episode ? Hotel.Hell.S01E01.720p.DD5.1.448kbps-ALANiS : options: --exclude audio_bit_rate -audio_bit_rate: 448Kbps ? Katy Perry - Pepsi & Billboard Summer Beats Concert Series 2012 1080i HDTV 20 Mbps DD2.0 MPEG2-TrollHD.ts : options: --exclude video_bit_rate -video_bit_rate: 20Mbps ? "[Figmentos] Monster 34 - At the End of Darkness [781219F1].mkv" : options: --exclude crc32 -crc32: 781219F1 ? 1080p25 : options: --exclude frame_rate screen_size: 1080p -frame_rate: 25fps ? 1080p25 : options: --exclude screen_size -screen_size: 1080p -frame_rate: 25fps ? 1080p25 : options: --include frame_rate -screen_size: 1080p -frame_rate: 25fps ? 1080p 30fps : options: --exclude screen_size -screen_size: 1080p frame_rate: 30fps guessit-3.8.0/guessit/test/episodes.yml000066400000000000000000003121551453655371700202340ustar00rootroot00000000000000? __default__ : type: episode ? Series/Californication/Season 2/Californication.2x05.Vaginatown.HDTV.XviD-0TV.avi : title: Californication season: 2 episode: 5 episode_title: Vaginatown source: HDTV video_codec: Xvid release_group: 0TV container: avi ? Series/dexter/Dexter.5x02.Hello,.Bandit.ENG.-.sub.FR.HDTV.XviD-AlFleNi-TeaM.[tvu.org.ru].avi : title: Dexter season: 5 episode: 2 episode_title: Hello, Bandit language: English subtitle_language: French source: HDTV video_codec: Xvid release_group: AlFleNi-TeaM website: tvu.org.ru container: avi ? Series/Treme/Treme.1x03.Right.Place,.Wrong.Time.HDTV.XviD-NoTV.avi : title: Treme season: 1 episode: 3 episode_title: Right Place, Wrong Time source: HDTV video_codec: Xvid release_group: NoTV ? Series/Duckman/Duckman - S1E13 Joking The Chicken (unedited).avi : title: Duckman season: 1 episode: 13 episode_title: Joking The Chicken ? Series/Simpsons/Saison 12 Français/Simpsons,.The.12x08.A.Bas.Le.Sergent.Skinner.FR.avi : title: The Simpsons season: 12 episode: 8 episode_title: A Bas Le Sergent Skinner language: French ? Series/Duckman/Duckman - 101 (01) - 20021107 - I, Duckman.avi : title: Duckman season: 1 episode: 1 episode_title: I, Duckman date: 2002-11-07 ? Series/Simpsons/Saison 12 Français/Simpsons,.The.12x08.A.Bas.Le.Sergent.Skinner.FR.avi : title: The Simpsons season: 12 episode: 8 episode_title: A Bas Le Sergent Skinner language: French ? Series/Futurama/Season 3 (mkv)/[™] Futurama - S03E22 - Le chef de fer à 30% ( 30 Percent Iron Chef ).mkv : title: Futurama season: 3 episode: 22 episode_title: Le chef de fer à 30% ? Series/The Office/Season 6/The Office - S06xE01.avi : title: The Office season: 6 episode: 1 ? series/The Office/Season 4/The Office [401] Fun Run.avi : title: The Office season: 4 episode: 1 episode_title: Fun Run ? Series/Mad Men Season 1 Complete/Mad.Men.S01E01.avi : title: Mad Men season: 1 episode: 1 other: Complete ? series/Psych/Psych S02 Season 2 Complete English DVD/Psych.S02E02.65.Million.Years.Off.avi : title: Psych season: 2 episode: 2 episode_title: 65 Million Years Off language: english source: DVD other: Complete ? series/Psych/Psych S02 Season 2 Complete English DVD/Psych.S02E03.Psy.Vs.Psy.Français.srt : title: Psych season: 2 episode: 3 episode_title: Psy Vs Psy source: DVD language: English subtitle_language: French other: Complete ? Series/Pure Laine/Pure.Laine.1x01.Toutes.Couleurs.Unies.FR.(Québec).DVB-Kceb.[tvu.org.ru].avi : title: Pure Laine season: 1 episode: 1 episode_title: Toutes Couleurs Unies source: Digital TV release_group: Kceb language: french website: tvu.org.ru ? Series/Pure Laine/2x05 - Pure Laine - Je Me Souviens.avi : title: Pure Laine season: 2 episode: 5 episode_title: Je Me Souviens ? Series/Tout sur moi/Tout sur moi - S02E02 - Ménage à trois (14-01-2008) [Rip by Ampli].avi : title: Tout sur moi season: 2 episode: 2 episode_title: Ménage à trois date: 2008-01-14 ? The.Mentalist.2x21.18-5-4.ENG.-.sub.FR.HDTV.XviD-AlFleNi-TeaM.[tvu.org.ru].avi : title: The Mentalist season: 2 episode: 21 episode_title: 18-5-4 language: english subtitle_language: french source: HDTV video_codec: Xvid release_group: AlFleNi-TeaM website: tvu.org.ru ? series/__ Incomplete __/Dr Slump (Catalan)/Dr._Slump_-_003_DVB-Rip_Catalan_by_kelf.avi : title: Dr Slump episode: 3 source: Digital TV other: Rip language: catalan # Disabling this test because it just doesn't looks like a serie ... #? series/Ren and Stimpy - Black_hole_[DivX].avi #: title: Ren and Stimpy # episode_title: Black hole # video_codec: DivX # Disabling this test because it just doesn't looks like a serie ... # ? Series/Walt Disney/Donald.Duck.-.Good.Scouts.[www.bigernie.jump.to].avi #: title: Donald Duck # episode_title: Good Scouts # website: www.bigernie.jump.to ? Series/Neverwhere/Neverwhere.05.Down.Street.[tvu.org.ru].avi : title: Neverwhere episode: 5 episode_title: Down Street website: tvu.org.ru ? Series/South Park/Season 4/South.Park.4x07.Cherokee.Hair.Tampons.DVDRip.[tvu.org.ru].avi : title: South Park season: 4 episode: 7 episode_title: Cherokee Hair Tampons source: DVD other: Rip website: tvu.org.ru ? Series/Kaamelott/Kaamelott - Livre V - Ep 23 - Le Forfait.avi : title: Kaamelott alternative_title: Livre V episode: 23 episode_title: Le Forfait ? Series/Duckman/Duckman - 110 (10) - 20021218 - Cellar Beware.avi : title: Duckman season: 1 episode: 10 date: 2002-12-18 episode_title: Cellar Beware # Removing this test because it doesn't look like a series # ? Series/Ren & Stimpy/Ren And Stimpy - Onward & Upward-Adult Party Cartoon.avi # : title: Ren And Stimpy # episode_title: Onward & Upward-Adult Party Cartoon ? Series/Breaking Bad/Minisodes/Breaking.Bad.(Minisodes).01.Good.Cop.Bad.Cop.WEBRip.XviD.avi : title: Breaking Bad episode_format: Minisode episode: 1 episode_title: Good Cop Bad Cop source: Web other: Rip video_codec: Xvid ? Series/My Name Is Earl/My.Name.Is.Earl.S01Extras.-.Bad.Karma.DVDRip.XviD.avi : title: My Name Is Earl season: 1 episode_title: Bad Karma source: DVD other: [Extras, Rip] video_codec: Xvid ? series/Freaks And Geeks/Season 1/Episode 4 - Kim Kelly Is My Friend-eng(1).srt : title: Freaks And Geeks season: 1 episode: 4 episode_title: Kim Kelly Is My Friend subtitle_language: English # This is really a subtitle_language, despite guessit 1.x assert for language. ? /mnt/series/The Big Bang Theory/S01/The.Big.Bang.Theory.S01E01.mkv : title: The Big Bang Theory season: 1 episode: 1 ? /media/Parks_and_Recreation-s03-e01.mkv : title: Parks and Recreation season: 3 episode: 1 ? /media/Parks_and_Recreation-s03-e02-Flu_Season.mkv : title: Parks and Recreation season: 3 episode_title: Flu Season episode: 2 ? /media/Parks_and_Recreation-s03-x01.mkv : title: Parks and Recreation season: 3 episode: 1 ? /media/Parks_and_Recreation-s03-x02-Gag_Reel.mkv : title: Parks and Recreation season: 3 episode: 2 episode_title: Gag Reel ? /media/Band_of_Brothers-e01-Currahee.mkv : title: Band of Brothers episode: 1 episode_title: Currahee ? /media/Band_of_Brothers-x02-We_Stand_Alone_Together.mkv : title: Band of Brothers bonus: 2 bonus_title: We Stand Alone Together ? /TV Shows/Mad.M-5x9.mkv : title: Mad M season: 5 episode: 9 ? /TV Shows/new.girl.117.hdtv-lol.mp4 : title: new girl season: 1 episode: 17 source: HDTV release_group: lol ? Kaamelott - 5x44x45x46x47x48x49x50.avi : title: Kaamelott season: 5 episode: [44, 45, 46, 47, 48, 49, 50] ? Example S01E01-02.avi ? Example S01E01E02.avi : title: Example season: 1 episode: [1, 2] ? Series/Baccano!/Baccano!_-_T1_-_Trailer_-_[Ayu](dae8173e).mkv : title: Baccano! other: Trailer release_group: Ayu episode_title: T1 crc32: dae8173e ? Series/Doctor Who (2005)/Season 06/Doctor Who (2005) - S06E01 - The Impossible Astronaut (1).avi : title: Doctor Who year: 2005 season: 6 episode: 1 episode_title: The Impossible Astronaut ? The Sopranos - [05x07] - In Camelot.mp4 : title: The Sopranos season: 5 episode: 7 episode_title: In Camelot ? The.Office.(US).1x03.Health.Care.HDTV.XviD-LOL.avi : title: The Office country: US season: 1 episode: 3 episode_title: Health Care source: HDTV video_codec: Xvid release_group: LOL ? /Volumes/data-1/Series/Futurama/Season 3/Futurama_-_S03_DVD_Bonus_-_Deleted_Scenes_Part_3.ogm : title: Futurama season: 3 part: 3 source: DVD other: Bonus ? Ben.and.Kate.S01E02.720p.HDTV.X264-DIMENSION.mkv : title: Ben and Kate season: 1 episode: 2 screen_size: 720p source: HDTV video_codec: H.264 release_group: DIMENSION ? /volume1/TV Series/Drawn Together/Season 1/Drawn Together 1x04 Requiem for a Reality Show.avi : title: Drawn Together season: 1 episode: 4 episode_title: Requiem for a Reality Show ? Sons.of.Anarchy.S05E06.720p.WEB.DL.DD5.1.H.264-CtrlHD.mkv : title: Sons of Anarchy season: 5 episode: 6 screen_size: 720p source: Web audio_channels: "5.1" audio_codec: Dolby Digital video_codec: H.264 release_group: CtrlHD ? /media/bdc64bfe-e36f-4af8-b550-e6fd2dfaa507/TV_Shows/Doctor Who (2005)/Saison 6/Doctor Who (2005) - S06E13 - The Wedding of River Song.mkv : title: Doctor Who season: 6 episode: 13 year: 2005 episode_title: The Wedding of River Song uuid: bdc64bfe-e36f-4af8-b550-e6fd2dfaa507 ? /mnt/videos/tvshows/Doctor Who/Season 06/E13 - The Wedding of River Song.mkv : title: Doctor Who season: 6 episode: 13 episode_title: The Wedding of River Song ? The.Simpsons.S24E03.Adventures.in.Baby-Getting.720p.WEB-DL.DD5.1.H.264-CtrlHD.mkv : title: The Simpsons season: 24 episode: 3 episode_title: Adventures in Baby-Getting screen_size: 720p source: Web audio_channels: "5.1" audio_codec: Dolby Digital video_codec: H.264 release_group: CtrlHD ? /home/disaster/Videos/TV/Merlin/merlin_2008.5x02.arthurs_bane_part_two.repack.720p_hdtv_x264-fov.mkv : title: merlin season: 5 episode: 2 part: 2 episode_title: arthurs bane screen_size: 720p source: HDTV video_codec: H.264 release_group: fov year: 2008 other: Proper proper_count: 1 ? "Da Vinci's Demons - 1x04 - The Magician.mkv" : title: "Da Vinci's Demons" season: 1 episode: 4 episode_title: The Magician ? CSI.S013E18.Sheltered.720p.WEB-DL.DD5.1.H.264.mkv : title: CSI season: 13 episode: 18 episode_title: Sheltered screen_size: 720p source: Web audio_channels: "5.1" audio_codec: Dolby Digital video_codec: H.264 ? Game of Thrones S03E06 1080i HDTV DD5.1 MPEG2-TrollHD.ts : title: Game of Thrones season: 3 episode: 6 screen_size: 1080i source: HDTV audio_channels: "5.1" audio_codec: Dolby Digital video_codec: MPEG-2 release_group: TrollHD ? gossip.girl.s01e18.hdtv.xvid-2hd.eng.srt : title: gossip girl season: 1 episode: 18 source: HDTV video_codec: Xvid release_group: 2hd subtitle_language: english ? Wheels.S03E01E02.720p.HDTV.x264-IMMERSE.mkv : title: Wheels season: 3 episode: [1, 2] screen_size: 720p source: HDTV video_codec: H.264 release_group: IMMERSE ? Wheels.S03E01-02.720p.HDTV.x264-IMMERSE.mkv : title: Wheels season: 3 episode: [1, 2] screen_size: 720p source: HDTV video_codec: H.264 release_group: IMMERSE ? Wheels.S03E01-E02.720p.HDTV.x264-IMMERSE.mkv : title: Wheels season: 3 episode: [1, 2] screen_size: 720p source: HDTV video_codec: H.264 release_group: IMMERSE ? Wheels.S03E01-04.720p.HDTV.x264-IMMERSE.mkv : title: Wheels season: 3 episode: [1, 2, 3, 4] screen_size: 720p source: HDTV video_codec: H.264 release_group: IMMERSE ? Marvels.Agents.of.S.H.I.E.L.D-S01E06.720p.HDTV.X264-DIMENSION.mkv : title: Marvels Agents of S.H.I.E.L.D season: 1 episode: 6 screen_size: 720p source: HDTV video_codec: H.264 release_group: DIMENSION ? Marvels.Agents.of.S.H.I.E.L.D.S01E06.720p.HDTV.X264-DIMENSION.mkv : title: Marvels Agents of S.H.I.E.L.D. season: 1 episode: 6 screen_size: 720p source: HDTV video_codec: H.264 release_group: DIMENSION ? Marvels.Agents.of.S.H.I.E.L.D..S01E06.720p.HDTV.X264-DIMENSION.mkv : title: Marvels Agents of S.H.I.E.L.D. season: 1 episode: 6 screen_size: 720p source: HDTV video_codec: H.264 release_group: DIMENSION ? Series/Friday Night Lights/Season 1/Friday Night Lights S01E19 - Ch-Ch-Ch-Ch-Changes.avi : title: Friday Night Lights season: 1 episode: 19 episode_title: Ch-Ch-Ch-Ch-Changes ? Dexter Saison VII FRENCH.BDRip.XviD-MiND.nfo : title: Dexter season: 7 video_codec: Xvid language: French source: Blu-ray other: Rip release_group: MiND ? Dexter Saison sept FRENCH.BDRip.XviD-MiND.nfo : title: Dexter season: 7 video_codec: Xvid language: French source: Blu-ray other: Rip release_group: MiND ? "Pokémon S16 - E29 - 1280*720 HDTV VF.mkv" : title: Pokémon source: HDTV language: French season: 16 episode: 29 screen_size: 720p ? One.Piece.E576.VOSTFR.720p.HDTV.x264-MARINE-FORD.mkv : episode: 576 video_codec: H.264 source: HDTV title: One Piece release_group: MARINE-FORD subtitle_language: French screen_size: 720p ? Dexter.S08E12.FINAL.MULTi.1080p.BluRay.x264-MiND.mkv : video_codec: H.264 episode: 12 season: 8 source: Blu-ray title: Dexter episode_details: Final language: Multiple languages release_group: MiND screen_size: 1080p ? One Piece - E623 VOSTFR HD [www.manga-ddl-free.com].mkv : website: www.manga-ddl-free.com episode: 623 subtitle_language: French title: One Piece other: HD ? Falling Skies Saison 1.HDLight.720p.x264.VFF.mkv : language: French screen_size: 720p season: 1 title: Falling Skies video_codec: H.264 other: Micro HD ? Sleepy.Hollow.S01E09.720p.WEB-DL.DD5.1.H.264-BP.mkv : episode: 9 video_codec: H.264 source: Web title: Sleepy Hollow audio_channels: "5.1" screen_size: 720p season: 1 # video_profile: BP # TODO: related to https://github.com/guessit-io/guessit/issues/458#issuecomment-305719715 audio_codec: Dolby Digital ? Sleepy.Hollow.S01E09.720p.WEB-DL.DD5.1.H.264-BS.mkv : episode: 9 video_codec: H.264 source: Web title: Sleepy Hollow audio_channels: "5.1" screen_size: 720p season: 1 release_group: BS audio_codec: Dolby Digital ? Battlestar.Galactica.S00.Pilot.FRENCH.DVDRip.XviD-NOTAG.avi : title: Battlestar Galactica season: 0 episode_details: Pilot episode_title: Pilot language: French source: DVD other: Rip video_codec: Xvid release_group: NOTAG ? The Big Bang Theory S00E00 Unaired Pilot VOSTFR TVRip XviD-VioCs : title: The Big Bang Theory season: 0 episode: 0 subtitle_language: French source: TV other: Rip video_codec: Xvid release_group: VioCs episode_details: [Unaired, Pilot] ? The Big Bang Theory S01E00 PROPER Unaired Pilot TVRip XviD-GIGGITY : title: The Big Bang Theory season: 1 episode: 0 source: TV video_codec: Xvid release_group: GIGGITY other: [Proper, Rip] proper_count: 1 episode_details: [Unaired, Pilot] ? Pawn.Stars.S2014E18.720p.HDTV.x264-KILLERS : title: Pawn Stars season: 2014 year: 2014 episode: 18 screen_size: 720p source: HDTV video_codec: H.264 release_group: KILLERS ? 2.Broke.Girls.S03E10.480p.HDTV.x264-mSD.mkv : title: 2 Broke Girls season: 3 episode: 10 screen_size: 480p source: HDTV video_codec: H.264 release_group: mSD ? the.100.109.hdtv-lol.mp4 : title: the 100 season: 1 episode: 9 source: HDTV release_group: lol ? Criminal.Minds.5x03.Reckoner.ENG.-.sub.FR.HDTV.XviD-STi.[tvu.org.ru].avi : title: Criminal Minds language: English subtitle_language: French season: 5 episode: 3 video_codec: Xvid source: HDTV website: tvu.org.ru release_group: STi episode_title: Reckoner ? 03-Criminal.Minds.avi : title: Criminal Minds episode: 3 ? '[Evil-Saizen]_Laughing_Salesman_14_[DVD][1C98686A].mkv' : crc32: 1C98686A episode: 14 source: DVD release_group: Evil-Saizen title: Laughing Salesman ? '[Kaylith] Zankyou no Terror - 04 [480p][B4D4514E].mp4' : crc32: B4D4514E episode: 4 release_group: Kaylith screen_size: 480p title: Zankyou no Terror ? '[PuyaSubs!] Seirei Tsukai no Blade Dance - 05 [720p][32DD560E].mkv' : crc32: 32DD560E episode: 5 release_group: PuyaSubs! screen_size: 720p title: Seirei Tsukai no Blade Dance ? '[Doremi].Happiness.Charge.Precure.27.[1280x720].[DC91581A].mkv' : crc32: DC91581A episode: 27 release_group: Doremi screen_size: 720p title: Happiness Charge Precure ? "[Daisei] Free!:Iwatobi Swim Club - 01 ~ (BD 720p 10-bit AAC) [99E8E009].mkv" : audio_codec: AAC crc32: 99E8E009 episode: 1 source: Blu-ray release_group: Daisei screen_size: 720p title: Free!:Iwatobi Swim Club color_depth: 10-bit ? '[Tsundere] Boku wa Tomodachi ga Sukunai - 03 [BDRip h264 1920x1080 10bit FLAC][AF0C22CC].mkv' : audio_codec: FLAC crc32: AF0C22CC episode: 3 source: Blu-ray release_group: Tsundere screen_size: 1080p title: Boku wa Tomodachi ga Sukunai video_codec: H.264 color_depth: 10-bit ? '[t.3.3.d]_Mikakunin_de_Shinkoukei_-_12_[720p][5DDC1352].mkv' : crc32: 5DDC1352 episode: 12 screen_size: 720p title: Mikakunin de Shinkoukei release_group: t.3.3.d ? '[Anime-Koi] Sabagebu! - 06 [h264-720p][ABB3728A].mkv' : crc32: ABB3728A episode: 6 release_group: Anime-Koi screen_size: 720p title: Sabagebu! video_codec: H.264 ? '[aprm-Diogo4D] [BD][1080p] Nagi no Asukara 08 [4D102B7C].mkv' : crc32: 4D102B7C episode: 8 source: Blu-ray release_group: aprm-Diogo4D screen_size: 1080p title: Nagi no Asukara ? '[Akindo-SSK] Zankyou no Terror - 05 [720P][Sub_ITA][F5CCE87C].mkv' : crc32: F5CCE87C episode: 5 release_group: Akindo-SSK screen_size: 720p title: Zankyou no Terror subtitle_language: it ? Naruto Shippuden Episode 366 VOSTFR.avi : episode: 366 title: Naruto Shippuden subtitle_language: fr ? Naruto Shippuden Episode 366v2 VOSTFR.avi : episode: 366 version: 2 title: Naruto Shippuden subtitle_language: fr ? '[HorribleSubs] Ao Haru Ride - 06 [480p].mkv' : episode: 6 release_group: HorribleSubs screen_size: 480p title: Ao Haru Ride ? '[DeadFish] Tari Tari - 01 [BD][720p][AAC].mp4' : audio_codec: AAC episode: 1 source: Blu-ray release_group: DeadFish screen_size: 720p title: Tari Tari ? '[NoobSubs] Sword Art Online II 06 (720p 8bit AAC).mp4' : audio_codec: AAC episode: 6 release_group: NoobSubs screen_size: 720p title: Sword Art Online II color_depth: 8-bit ? '[DeadFish] 01 - Tari Tari [BD][720p][AAC].mp4' : audio_codec: AAC episode: 1 source: Blu-ray release_group: DeadFish screen_size: 720p title: Tari Tari ? '[NoobSubs] 06 Sword Art Online II (720p 8bit AAC).mp4' : audio_codec: AAC episode: 6 release_group: NoobSubs screen_size: 720p title: Sword Art Online II color_depth: 8-bit ? '[DeadFish] 12 - Tari Tari [BD][720p][AAC].mp4' : audio_codec: AAC episode: 12 source: Blu-ray release_group: DeadFish screen_size: 720p title: Tari Tari ? Something.Season.2.1of4.Ep.Title.HDTV.torrent : episode_count: 4 episode: 1 source: HDTV season: 2 title: Something episode_title: Title container: torrent ? Something.Season.2of5.3of9.Ep.Title.HDTV.torrent : episode_count: 9 episode: 3 source: HDTV season: 2 season_count: 5 title: Something episode_title: Title container: torrent ? Something.Other.Season.3of5.Complete.HDTV.torrent : source: HDTV other: Complete season: 3 season_count: 5 title: Something Other container: torrent ? Something.Other.Season.1-3.avi : season: [1, 2, 3] title: Something Other ? Something.Other.Season.1&3.avi : season: [1, 3] title: Something Other ? Something.Other.Season.1&3-1to12ep.avi : season: [1, 3] title: Something Other ? W2Test.123.HDTV.XViD-FlexGet : episode: 23 season: 1 source: HDTV release_group: FlexGet title: W2Test video_codec: Xvid ? W2Test.123.HDTV.XViD-FlexGet : options: --episode-prefer-number episode: 123 source: HDTV release_group: FlexGet title: W2Test video_codec: Xvid ? FooBar.0307.PDTV-FlexGet : episode: 7 source: Digital TV release_group: FlexGet season: 3 title: FooBar ? FooBar.0307.PDTV-FlexGet ? FooBar.307.PDTV-FlexGet : options: --episode-prefer-number episode: 307 source: Digital TV release_group: FlexGet title: FooBar ? FooBar.07.PDTV-FlexGet : episode: 7 source: Digital TV release_group: FlexGet title: FooBar ? FooBar.7.PDTV-FlexGet : episode: 7 source: Digital TV release_group: FlexGet title: FooBar ? FooBar.0307.PDTV-FlexGet : episode: 7 source: Digital TV release_group: FlexGet season: 3 title: FooBar ? FooBar.307.PDTV-FlexGet : episode: 7 source: Digital TV release_group: FlexGet season: 3 title: FooBar ? FooBar.07.PDTV-FlexGet : episode: 7 source: Digital TV release_group: FlexGet title: FooBar ? FooBar.07v4.PDTV-FlexGet : episode: 7 version: 4 source: Digital TV release_group: FlexGet title: FooBar ? FooBar.7.PDTV-FlexGet : source: Digital TV release_group: FlexGet title: FooBar 7 type: movie ? FooBar.7.PDTV-FlexGet : options: -t episode episode: 7 source: Digital TV release_group: FlexGet title: FooBar ? FooBar.7v3.PDTV-FlexGet : options: -t episode episode: 7 version: 3 source: Digital TV release_group: FlexGet title: FooBar ? Test.S02E01.hdtv.real.proper : episode: 1 source: HDTV other: Proper proper_count: 2 season: 2 title: Test ? Real.Test.S02E01.hdtv.proper : episode: 1 source: HDTV other: Proper proper_count: 1 season: 2 title: Real Test ? Test.Real.S02E01.hdtv.proper : episode: 1 source: HDTV other: Proper proper_count: 1 season: 2 title: Test Real ? Test.S02E01.hdtv.proper : episode: 1 source: HDTV other: Proper proper_count: 1 season: 2 title: Test ? Test.S02E01.hdtv.real.repack.proper : episode: 1 source: HDTV other: Proper proper_count: 3 season: 2 title: Test ? Date.Show.03-29-2012.HDTV.XViD-FlexGet : date: 2012-03-29 source: HDTV release_group: FlexGet title: Date Show video_codec: Xvid ? Something.1x5.Season.Complete-FlexGet : episode: 5 other: Complete season: 1 title: Something release_group: FlexGet ? Something Seasons 1 & 2 - Complete : other: Complete season: - 1 - 2 title: Something ? Something Seasons 4 Complete : other: Complete season: 4 title: Something ? Something.1xAll.Season.Complete-FlexGet : other: Complete season: 1 title: Something release_group: FlexGet ? Something.1xAll-FlexGet : other: Complete season: 1 title: Something release_group: FlexGet ? FlexGet.US.S2013E14.Title.Here.720p.HDTV.AAC5.1.x264-NOGRP : audio_channels: '5.1' audio_codec: AAC country: US episode: 14 source: HDTV release_group: NOGRP screen_size: 720p season: 2013 title: FlexGet episode_title: Title Here video_codec: H.264 year: 2013 ? FlexGet.14.of.21.Title.Here.720p.HDTV.AAC5.1.x264-NOGRP : audio_channels: '5.1' audio_codec: AAC episode_count: 21 episode: 14 source: HDTV release_group: NOGRP screen_size: 720p title: FlexGet episode_title: Title Here video_codec: H.264 ? FlexGet.Series.2013.14.of.21.Title.Here.720p.HDTV.AAC5.1.x264-NOGRP : audio_channels: '5.1' audio_codec: AAC episode_count: 21 episode: 14 source: HDTV release_group: NOGRP screen_size: 720p season: 2013 title: FlexGet Series episode_title: Title Here video_codec: H.264 year: 2013 ? Something.S04E05E09 : episode: # 1.x guessit this as a range from 5 to 9. But not sure if it should ... - 5 - 9 season: 4 title: Something ? FooBar 360 1080i : options: --episode-prefer-number episode: 360 screen_size: 1080i title: FooBar ? FooBar 360 1080i : episode: 60 season: 3 screen_size: 1080i title: FooBar ? FooBar 360 : season: 3 episode: 60 title: FooBar -screen_size: 360p ? BarFood christmas special HDTV : options: --expected-title BarFood source: HDTV title: BarFood episode_title: christmas special episode_details: Special ? Something.2008x12.13-FlexGet : title: Something date: 2008-12-13 episode_title: FlexGet ? '[Ignored] Test 12' : episode: 12 release_group: Ignored title: Test ? '[FlexGet] Test 12' : episode: 12 release_group: FlexGet title: Test ? Test.13.HDTV-Ignored : episode: 13 source: HDTV release_group: Ignored title: Test ? Test.13.HDTV-Ignored : options: --expected-series test episode: 13 source: HDTV release_group: Ignored title: Test ? Test.13.HDTV-Ignored : title: Test episode: 13 source: HDTV release_group: Ignored ? Test.13.HDTV-Ignored : episode: 13 source: HDTV release_group: Ignored title: Test ? Test.13.HDTV-FlexGet : episode: 13 source: HDTV release_group: FlexGet title: Test ? Test.14.HDTV-Name : episode: 14 source: HDTV release_group: Name title: Test ? Real.Time.With.Bill.Maher.2014.10.31.HDTV.XviD-AFG.avi : date: 2014-10-31 source: HDTV release_group: AFG title: Real Time With Bill Maher video_codec: Xvid ? Arrow.S03E21.Al.Sah-Him.1080p.WEB-DL.DD5.1.H.264-BS.mkv : title: Arrow season: 3 episode: 21 episode_title: Al Sah-Him screen_size: 1080p audio_codec: Dolby Digital audio_channels: "5.1" video_codec: H.264 release_group: BS source: Web ? How to Make It in America - S02E06 - I'm Sorry, Who's Yosi?.mkv : title: How to Make It in America season: 2 episode: 6 episode_title: I'm Sorry, Who's Yosi? ? 24.S05E07.FRENCH.DVDRip.XviD-FiXi0N.avi : episode: 7 source: DVD other: Rip language: fr season: 5 title: '24' video_codec: Xvid release_group: FiXi0N ? 12.Monkeys.S01E12.FRENCH.BDRip.x264-VENUE.mkv : episode: 12 source: Blu-ray other: Rip language: fr release_group: VENUE season: 1 title: 12 Monkeys video_codec: H.264 ? 90.Day.Fiance.S02E07.I.Have.To.Tell.You.Something.720p.HDTV.x264-W4F : episode: 7 source: HDTV screen_size: 720p season: 2 title: 90 Day Fiance episode_title: I Have To Tell You Something release_group: W4F ? Doctor.Who.2005.S04E06.FRENCH.LD.DVDRip.XviD-TRACKS.avi : episode: 6 source: DVD language: fr release_group: TRACKS season: 4 title: Doctor Who other: [Line Dubbed, Rip] video_codec: Xvid year: 2005 ? Astro.Le.Petit.Robot.S01E01+02.FRENCH.DVDRiP.X264.INT-BOOLZ.mkv : episode: [1, 2] source: DVD other: Rip language: fr release_group: INT-BOOLZ season: 1 title: Astro Le Petit Robot video_codec: H.264 ? Annika.Bengtzon.2012.E01.Le.Testament.De.Nobel.FRENCH.DVDRiP.XViD-STVFRV.avi : episode: 1 source: DVD other: Rip language: fr release_group: STVFRV title: Annika Bengtzon episode_title: Le Testament De Nobel video_codec: Xvid year: 2012 ? Dead.Set.02.FRENCH.LD.DVDRip.XviD-EPZ.avi : episode: 2 source: DVD language: fr other: [Line Dubbed, Rip] release_group: EPZ title: Dead Set video_codec: Xvid ? Phineas and Ferb S01E00 & S01E01 & S01E02 : episode: [0, 1, 2] season: 1 title: Phineas and Ferb ? Show.Name.S01E02.S01E03.HDTV.XViD.Etc-Group : episode: [2, 3] source: HDTV release_group: Etc-Group season: 1 title: Show Name video_codec: Xvid ? Show Name - S01E02 - S01E03 - S01E04 - Ep Name : episode: [2, 3, 4] season: 1 title: Show Name episode_title: Ep Name ? Show.Name.1x02.1x03.HDTV.XViD.Etc-Group : episode: [2, 3] source: HDTV release_group: Etc-Group season: 1 title: Show Name video_codec: Xvid ? Show Name - 1x02 - 1x03 - 1x04 - Ep Name : episode: [2, 3, 4] season: 1 title: Show Name episode_title: Ep Name ? Show.Name.S01E02.HDTV.XViD.Etc-Group : episode: 2 source: HDTV release_group: Etc-Group season: 1 title: Show Name video_codec: Xvid ? Show Name - S01E02 - My Ep Name : episode: 2 season: 1 title: Show Name episode_title: My Ep Name ? Show Name - S01.E03 - My Ep Name : episode: 3 season: 1 title: Show Name episode_title: My Ep Name ? Show.Name.S01E02E03.HDTV.XViD.Etc-Group : episode: [2, 3] source: HDTV release_group: Etc-Group season: 1 title: Show Name video_codec: Xvid ? Show Name - S01E02-03 - My Ep Name : episode: [2, 3] season: 1 title: Show Name episode_title: My Ep Name ? Show.Name.S01.E02.E03 : episode: [2, 3] season: 1 title: Show Name ? Show_Name.1x02.HDTV_XViD_Etc-Group : episode: 2 source: HDTV release_group: Etc-Group season: 1 title: Show Name video_codec: Xvid ? Show Name - 1x02 - My Ep Name : episode: 2 season: 1 title: Show Name episode_title: My Ep Name ? Show_Name.1x02x03x04.HDTV_XViD_Etc-Group : episode: [2, 3, 4] source: HDTV release_group: Etc-Group season: 1 title: Show Name video_codec: Xvid ? Show Name - 1x02-03-04 - My Ep Name : episode: [2, 3, 4] season: 1 title: Show Name episode_title: My Ep Name # 1x guess this as episode 100 but 101 as episode 1 season 1. ? Show.Name.100.Event.2010.11.23.HDTV.XViD.Etc-Group : date: 2010-11-23 season: 1 episode: 0 source: HDTV release_group: Etc-Group title: Show Name episode_title: Event video_codec: Xvid ? Show.Name.101.Event.2010.11.23.HDTV.XViD.Etc-Group : date: 2010-11-23 season: 1 episode: 1 source: HDTV release_group: Etc-Group title: Show Name episode_title: Event video_codec: Xvid ? Show.Name.2010.11.23.HDTV.XViD.Etc-Group : date: 2010-11-23 source: HDTV release_group: Etc-Group title: Show Name ? Show Name - 2010-11-23 - Ep Name : date: 2010-11-23 title: Show Name episode_title: Ep Name ? Show Name Season 1 Episode 2 Ep Name : episode: 2 season: 1 title: Show Name episode_title: Ep Name ? Show.Name.S01.HDTV.XViD.Etc-Group : source: HDTV release_group: Etc-Group season: 1 title: Show Name video_codec: Xvid ? Show.Name.E02-03 : episode: [2, 3] title: Show Name ? Show.Name.E02.2010 : episode: 2 year: 2010 title: Show Name ? Show.Name.E23.Test : episode: 23 title: Show Name episode_title: Test ? Show.Name.Part.3.HDTV.XViD.Etc-Group : part: 3 title: Show Name source: HDTV video_codec: Xvid release_group: Etc-Group type: movie # Fallback to movie type because we can't tell it's a series ... ? Show.Name.Part.1.and.Part.2.Blah-Group : part: [1, 2] title: Show Name type: movie # Fallback to movie type because we can't tell it's a series ... ? Show Name - 01 - Ep Name : episode: 1 title: Show Name episode_title: Ep Name ? 01 - Ep Name : episode: 1 title: Ep Name ? Show.Name.102.HDTV.XViD.Etc-Group : episode: 2 source: HDTV release_group: Etc-Group season: 1 title: Show Name video_codec: Xvid ? '[HorribleSubs] Maria the Virgin Witch - 01 [720p].mkv' : episode: 1 release_group: HorribleSubs screen_size: 720p title: Maria the Virgin Witch ? '[ISLAND]One_Piece_679_[VOSTFR]_[V1]_[8bit]_[720p]_[EB7838FC].mp4' : crc32: EB7838FC episode: 679 release_group: ISLAND screen_size: 720p title: One Piece subtitle_language: fr color_depth: 8-bit version: 1 ? '[ISLAND]One_Piece_679_[VOSTFR]_[8bit]_[720p]_[EB7838FC].mp4' : crc32: EB7838FC episode: 679 release_group: ISLAND screen_size: 720p title: One Piece subtitle_language: fr color_depth: 8-bit ? '[Kaerizaki-Fansub]_One_Piece_679_[VOSTFR][HD_1280x720].mp4' : episode: 679 other: HD release_group: Kaerizaki-Fansub screen_size: 720p title: One Piece subtitle_language: fr ? '[Kaerizaki-Fansub]_One_Piece_679_[VOSTFR][FANSUB][HD_1280x720].mp4' : episode: 679 other: [Fan Subtitled, HD] release_group: Kaerizaki-Fansub screen_size: 720p title: One Piece subtitle_language: fr ? '[Kaerizaki-Fansub]_One_Piece_681_[VOSTFR][HD_1280x720]_V2.mp4' : episode: 681 other: HD release_group: Kaerizaki-Fansub screen_size: 720p title: One Piece subtitle_language: fr version: 2 ? '[Kaerizaki-Fansub] High School DxD New 04 VOSTFR HD (1280x720) V2.mp4' : episode: 4 other: HD release_group: Kaerizaki-Fansub screen_size: 720p title: High School DxD New subtitle_language: fr version: 2 ? '[Kaerizaki-Fansub] One Piece 603 VOSTFR PS VITA (960x544) V2.mp4' : episode: 603 release_group: Kaerizaki-Fansub other: PS Vita screen_size: 960x544 title: One Piece subtitle_language: fr version: 2 ? '[Group Name] Show Name.13' : episode: 13 release_group: Group Name title: Show Name ? '[Group Name] Show Name - 13' : episode: 13 release_group: Group Name title: Show Name ? '[Group Name] Show Name 13' : episode: 13 release_group: Group Name title: Show Name # [Group Name] Show Name.13-14 # [Group Name] Show Name - 13-14 # Show Name 13-14 ? '[Stratos-Subs]_Infinite_Stratos_-_12_(1280x720_H.264_AAC)_[379759DB]' : audio_codec: AAC crc32: 379759DB episode: 12 release_group: Stratos-Subs screen_size: 720p title: Infinite Stratos video_codec: H.264 # [ShinBunBu-Subs] Bleach - 02-03 (CX 1280x720 x264 AAC) ? '[SGKK] Bleach 312v1 [720p/MKV]' : episode: 312 release_group: SGKK screen_size: 720p title: Bleach version: 1 ? '[Ayako]_Infinite_Stratos_-_IS_-_07_[H264][720p][EB7838FC]' : crc32: EB7838FC episode: 7 release_group: Ayako screen_size: 720p title: Infinite Stratos video_codec: H.264 ? '[Ayako] Infinite Stratos - IS - 07v2 [H264][720p][44419534]' : crc32: '44419534' episode: 7 release_group: Ayako screen_size: 720p title: Infinite Stratos video_codec: H.264 version: 2 ? '[Ayako-Shikkaku] Oniichan no Koto Nanka Zenzen Suki Janain Dakara ne - 10 [LQ][h264][720p] [8853B21C]' : crc32: 8853B21C episode: 10 release_group: Ayako-Shikkaku screen_size: 720p title: Oniichan no Koto Nanka Zenzen Suki Janain Dakara ne video_codec: H.264 ? Bleach - s16e03-04 - 313-314 ? Bleach.s16e03-04.313-314-GROUP ? Bleach s16e03e04 313-314 : title: Bleach season: 16 episode: [3, 4] absolute_episode: [313, 314] ? Bleach - 313-314 : options: -E episode: [313, 314] title: Bleach ? '[ShinBunBu-Subs] Bleach - 02-03 (CX 1280x720 x264 AAC)' : audio_codec: AAC episode: [2, 3] release_group: ShinBunBu-Subs screen_size: 720p title: Bleach video_codec: H.264 ? 003. Show Name - Ep Name.avi : episode: 3 title: Show Name episode_title: Ep Name ? 003-004. Show Name - Ep Name.avi : episode: [3, 4] title: Show Name episode_title: Ep Name ? One Piece - 102 : episode: 2 season: 1 title: One Piece ? "[ACX]_Wolf's_Spirit_001.mkv" : episode: 1 release_group: ACX title: "Wolf's Spirit" ? Project.Runway.S14E00.and.S14E01.(Eng.Subs).SDTV.x264-[2Maverick].mp4 : episode: [0, 1] source: TV release_group: 2Maverick season: 14 title: Project Runway subtitle_language: en video_codec: H.264 ? '[Hatsuyuki-Kaitou]_Fairy_Tail_2_-_16-20_[720p][10bit].torrent' : episode: [16, 17, 18, 19, 20] release_group: Hatsuyuki-Kaitou screen_size: 720p title: Fairy Tail 2 color_depth: 10-bit ? '[Hatsuyuki-Kaitou]_Fairy_Tail_2_-_16-20_(191-195)_[720p][10bit].torrent' : episode: [16, 17, 18, 19, 20] absolute_episode: [191, 192, 193, 194, 195] release_group: Hatsuyuki-Kaitou screen_size: 720p title: Fairy Tail 2 ? "Looney Tunes 1940x01 Porky's Last Stand.mkv" : episode: 1 season: 1940 title: Looney Tunes episode_title: Porky's Last Stand year: 1940 ? The.Good.Wife.S06E01.E10.720p.WEB-DL.DD5.1.H.264-CtrlHD/The.Good.Wife.S06E09.Trust.Issues.720p.WEB-DL.DD5.1.H.264-CtrlHD.mkv : audio_channels: '5.1' audio_codec: Dolby Digital episode: 9 source: Web release_group: CtrlHD screen_size: 720p season: 6 title: The Good Wife episode_title: Trust Issues video_codec: H.264 ? Fear the Walking Dead - 01x02 - So Close, Yet So Far.REPACK-KILLERS.French.C.updated.Addic7ed.com.mkv : episode: 2 language: fr other: Proper proper_count: 1 season: 1 title: Fear the Walking Dead episode_title: So Close, Yet So Far ? Fear the Walking Dead - 01x02 - En Close, Yet En Far.REPACK-KILLERS.French.C.updated.Addic7ed.com.mkv : episode: 2 language: fr other: Proper proper_count: 1 season: 1 title: Fear the Walking Dead episode_title: En Close, Yet En Far ? /av/unsorted/The.Daily.Show.2015.07.22.Jake.Gyllenhaal.720p.HDTV.x264-BATV.mkv : date: 2015-07-22 source: HDTV release_group: BATV screen_size: 720p title: The Daily Show episode_title: Jake Gyllenhaal video_codec: H.264 ? "[7.1.7.8.5] Foo Bar - 11 (H.264) [5235532D].mkv" : episode: 11 ? my 720p show S01E02 : options: -T "my 720p show" title: my 720p show season: 1 episode: 2 ? my 720p show S01E02 720p : options: -T "my 720p show" title: my 720p show season: 1 episode: 2 screen_size: 720p ? -my 720p show S01E02 : options: -T "re:my \d+p show" screen_size: 720p ? Show S01E02 : options: -T "The Show" title: Show season: 1 episode: 2 ? Foo's & Bars (2009) S01E01 720p XviD-2HD[AOEU] : episode: 1 release_group: 2HD[AOEU] screen_size: 720p season: 1 title: Foo's & Bars video_codec: Xvid year: 2009 ? Date.Series.10-11-2008.XViD : date: 2008-11-10 title: Date Series video_codec: Xvid ? Scrubs/SEASON-06/Scrubs.S06E09.My.Perspective.DVDRip.XviD-WAT/scrubs.s06e09.dvdrip.xvid-wat.avi : container: avi episode: 9 episode_title: My Perspective source: DVD other: Rip release_group: WAT season: 6 title: Scrubs video_codec: Xvid ? '[PuyaSubs!] Digimon Adventure tri - 01 [720p][F9967949].mkv' : container: mkv crc32: F9967949 episode: 1 release_group: PuyaSubs! screen_size: 720p title: Digimon Adventure tri ? Sherlock.S01.720p.BluRay.x264-AVCHD : source: Blu-ray screen_size: 720p season: 1 title: Sherlock video_codec: H.264 ? Running.Wild.With.Bear.Grylls.S02E07.Michael.B.Jordan.PROPER.HDTV.x264-W4F.avi : container: avi episode: 7 episode_title: Michael B Jordan source: HDTV other: Proper proper_count: 1 release_group: W4F season: 2 title: Running Wild With Bear Grylls video_codec: H.264 ? Homeland.S05E11.Our.Man.in.Damascus.German.Sub.720p.HDTV.x264.iNTERNAL-BaCKToRG : episode: 11 episode_title: Our Man in Damascus source: HDTV other: Internal release_group: BaCKToRG screen_size: 720p season: 5 subtitle_language: de title: Homeland type: episode video_codec: H.264 ? Breaking.Bad.S01E01.2008.BluRay.VC1.1080P.5.1.WMV-NOVO : title: Breaking Bad season: 1 episode: 1 year: 2008 source: Blu-ray screen_size: 1080p audio_channels: '5.1' container: WMV release_group: NOVO type: episode ? Cosmos.A.Space.Time.Odyssey.S01E02.HDTV.x264.PROPER-LOL : title: Cosmos A Space Time Odyssey season: 1 episode: 2 source: HDTV video_codec: H.264 other: Proper proper_count: 1 release_group: LOL type: episode ? Fear.The.Walking.Dead.S02E01.HDTV.x264.AAC.MP4-k3n : title: Fear The Walking Dead season: 2 episode: 1 source: HDTV video_codec: H.264 audio_codec: AAC container: mp4 release_group: k3n type: episode ? Elementary.S01E01.Pilot.DVDSCR.x264.PREAiR-NoGRP : title: Elementary season: 1 episode: 1 episode_details: Pilot episode_title: Pilot source: DVD video_codec: H.264 other: [Screener, Preair] release_group: NoGRP type: episode ? Once.Upon.a.Time.S05E19.HDTV.x264.REPACK-LOL[ettv] : title: Once Upon a Time season: 5 episode: 19 source: HDTV video_codec: H.264 other: Proper proper_count: 1 release_group: LOL[ettv] type: episode ? Show.Name.S01E03.WEB-DL.x264.HUN-nIk : title: Show Name season: 1 episode: 3 source: Web video_codec: H.264 language: hu release_group: nIk type: episode ? Game.of.Thrones.S6.Ep5.X265.Dolby.2.0.KTM3.mp4 : audio_channels: '2.0' audio_codec: Dolby Digital container: mp4 episode: 5 release_group: KTM3 season: 6 title: Game of Thrones type: episode video_codec: H.265 ? Fargo.-.Season.1.-.720p.BluRay.-.x264.-.ShAaNiG : source: Blu-ray release_group: ShAaNiG screen_size: 720p season: 1 title: Fargo type: episode video_codec: H.264 ? Show.Name.S02E02.Episode.Title.1080p.WEB-DL.x264.5.1Ch.-.Group : audio_channels: '5.1' episode: 2 episode_title: Episode Title source: Web release_group: Group screen_size: 1080p season: 2 title: Show Name type: episode video_codec: H.264 ? Breaking.Bad.S01E01.2008.BluRay.VC1.1080P.5.1.WMV-NOVO : audio_channels: '5.1' container: wmv episode: 1 source: Blu-ray release_group: NOVO screen_size: 1080p season: 1 title: Breaking Bad type: episode year: 2008 ? Cosmos.A.Space.Time.Odyssey.S01E02.HDTV.x264.PROPER-LOL : episode: 2 source: HDTV other: Proper proper_count: 1 release_group: LOL season: 1 title: Cosmos A Space Time Odyssey type: episode video_codec: H.264 ? Elementary.S01E01.Pilot.DVDSCR.x264.PREAiR-NoGRP : episode: 1 episode_details: Pilot episode_title: Pilot source: DVD other: - Screener - Preair release_group: NoGRP season: 1 title: Elementary type: episode video_codec: H.264 ? Fear.The.Walking.Dead.S02E01.HDTV.x264.AAC.MP4-k3n.mp4 : audio_codec: AAC container: mp4 episode: 1 source: HDTV release_group: k3n season: 2 title: Fear The Walking Dead type: episode video_codec: H.264 ? Game.of.Thrones.S03.1080p.BluRay.DTS-HD.MA.5.1.AVC.REMUX-FraMeSToR : audio_channels: '5.1' audio_codec: DTS-HD audio_profile: Master Audio source: Blu-ray other: Remux release_group: FraMeSToR screen_size: 1080p season: 3 title: Game of Thrones type: episode ? Show.Name.S01E02.HDTV.x264.NL-subs-ABC : episode: 2 source: HDTV release_group: ABC season: 1 subtitle_language: nl title: Show Name type: episode video_codec: H.264 ? Friends.S01-S10.COMPLETE.720p.BluRay.x264-PtM : source: Blu-ray other: Complete release_group: PtM screen_size: 720p season: # Should it be [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ? - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 title: Friends type: episode video_codec: H.264 ? Duck.Dynasty.S02E07.Streik.German.DOKU.DL.WS.DVDRiP.x264-CDP : episode: 7 episode_title: Streik source: DVD language: - German - Multi other: [Documentary, Widescreen, Rip] release_group: CDP season: 2 title: Duck Dynasty type: episode video_codec: H.264 ? Family.Guy.S13E14.JOLO.German.AC3D.DL.720p.WebHD.x264-CDD : audio_codec: Dolby Digital episode: 14 episode_title: JOLO source: Web language: - German - Multi release_group: CDD screen_size: 720p season: 13 title: Family Guy type: episode video_codec: H.264 ? How.I.Met.Your.Mother.COMPLETE.SERIES.DVDRip.XviD-AR : options: -L en -C us source: DVD other: [Complete, Rip] release_group: AR title: How I Met Your Mother type: movie # Should be episode video_codec: Xvid ? Show Name The Complete Seasons 1 to 5 720p BluRay x265 HEVC-SUJAIDR[UTR] : source: Blu-ray other: Complete release_group: SUJAIDR[UTR] screen_size: 720p season: - 1 - 2 - 3 - 4 - 5 title: Show Name type: episode video_codec: H.265 ? Fear.the.Walking.Dead.-.Season.2.epi.02.XviD.Eng.Ac3-5.1.sub.ita.eng.iCV-MIRCrew : options: -t episode audio_channels: '5.1' audio_codec: Dolby Digital episode: 2 episode_title: epi language: en release_group: iCV-MIRCrew season: 2 subtitle_language: it title: Fear the Walking Dead type: episode video_codec: Xvid ? Game.Of.Thrones.S06E04.720p.PROPER.HDTV.x264-HDD : episode: 4 source: HDTV other: Proper proper_count: 1 release_group: HDD screen_size: 720p season: 6 title: Game Of Thrones type: episode video_codec: H.264 ? Marvels.Daredevil.S02E04.WEBRip.x264-NF69.mkv : container: mkv episode: 4 source: Web other: Rip release_group: NF69 season: 2 title: Marvels Daredevil type: episode video_codec: H.264 ? The.Walking.Dead.S06E01.FRENCH.1080p.WEB-DL.DD5.1.HEVC.x265-GOLF68 : audio_channels: '5.1' audio_codec: Dolby Digital episode: 1 source: Web language: fr release_group: GOLF68 screen_size: 1080p season: 6 title: The Walking Dead type: episode video_codec: H.265 ? American.Crime.S01E03.FASTSUB.VOSTFR.720p.HDTV.x264-F4ST : episode: 3 source: HDTV other: Fast Subtitled release_group: F4ST screen_size: 720p season: 1 subtitle_language: fr title: American Crime type: episode video_codec: H.264 ? Gotham.S02E12.FASTSUB.VOSTFR.HDTV.X264-F4ST3R : episode: 12 source: HDTV other: Fast Subtitled release_group: F4ST3R season: 2 subtitle_language: fr title: Gotham type: episode video_codec: H.264 # WEBRip + LD ? Australian.Story.2016.05.23.Into.The.Fog.of.War.Part.1.360p.LDTV.WEBRIP.[MPup] : title: Australian Story date: 2016-05-23 episode_title: Into The Fog of War part: 1 screen_size: 360p other: [Low Definition, Rip] source: Web release_group: MPup type: episode # AHDTV ? Show.Name.S04E06.FRENCH.AHDTV.XviD : title: Show Name season: 4 episode: 6 language: fr source: Analog HDTV video_codec: Xvid type: episode # WEBDLRip ? Show.Name.s06e14.WEBDLRip.-qqss44.avi : title: Show Name season: 6 episode: 14 source: Web other: Rip release_group: qqss44 container: avi type: episode # WEBCap ? Steven.Universe.S03E06.Steven.Floats.720p.WEBCap.x264-SRS : title: Steven Universe season: 3 episode: 6 episode_title: Steven Floats screen_size: 720p source: Web other: Rip video_codec: H.264 release_group: SRS type: episode # DSR ? Show.Name.S05E09.Some.Episode.Title.WS.DSR.x264-[NY2] : title: Show Name season: 5 episode: 9 episode_title: Some Episode Title other: Widescreen source: Satellite video_codec: H.264 release_group: NY2 type: episode # DSRip ? Squidbillies.S04E05.WS.DSRip.XviD-aAF : title: Squidbillies season: 4 episode: 5 other: [Widescreen, Rip] source: Satellite video_codec: Xvid release_group: aAF type: episode ? /series/The.B*.B*.T*.S10E01.1080p.HDTV.X264-DIMENSION[rarbg]/The.B*.B*.T*.S10E01.1080p.HDTV.X264-DIMENSION.mkv : container: mkv episode: 1 source: HDTV release_group: DIMENSION screen_size: 1080p season: 10 title: The B B T type: episode video_codec: H.264 ? '[Y-F] Very long Show Name Here - 03 Vostfr HD 8bits' : release_group: Y-F title: Very long Show Name Here episode: 3 subtitle_language: fr other: HD color_depth: 8-bit type: episode ? '[.www.site.com.].-.Snooze.and.Go.Sleep.S03E02.1080p.HEVC.x265-MeGusta' : episode: 2 release_group: MeGusta screen_size: 1080p season: 3 title: Snooze and Go Sleep type: episode video_codec: H.265 website: www.site.com ? Show.Name.S01.720p.HDTV.DD5.1.x264-Group/show.name.0106.720p-group.mkv : title: Show Name season: 1 screen_size: 720p source: HDTV audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: Group episode: 6 container: mkv type: episode ? Coupling Season 1 - 4 Complete DVDRip/Coupling Season 4/Coupling - (4x03) - Bed Time.mkv : title: Coupling other: [Complete, Rip] source: DVD season: 4 episode: 3 episode_title: Bed Time container: mkv type: episode ? Vice.News.Tonight.2016.10.10.1080p.HBO.WEBRip.AAC2.0.H.264-monkee : title: Vice News Tonight date: 2016-10-10 screen_size: 1080p source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode ? frasier.s8e6-768660.srt : container: srt episode: 6 episode_title: '768660' season: 8 title: frasier type: episode ? Show.Name.S03E15.480p.177mb.Proper.HDTV.x264 : title: Show Name season: 3 episode: 15 screen_size: 480p size: 177MB other: Proper proper_count: 1 source: HDTV video_codec: H.264 type: episode ? Show.Name.S03E15.480p.4.8GB.Proper.HDTV.x264 : title: Show Name season: 3 episode: 15 screen_size: 480p size: 4.8GB other: Proper proper_count: 1 source: HDTV video_codec: H.264 type: episode ? Show.Name.S03.1.1TB.Proper.HDTV.x264 : title: Show Name season: 3 size: 1.1TB other: Proper proper_count: 1 source: HDTV video_codec: H.264 type: episode ? Some.Show.S02E14.1080p.HDTV.X264-reenc.GROUP ? Some.Show.S02E14.1080p.HDTV.X264-re-enc.GROUP ? Some.Show.S02E14.1080p.HDTV.X264-re-encoded.GROUP ? Some.Show.S02E14.1080p.HDTV.X264-reencoded.GROUP : title: Some Show season: 2 episode: 14 screen_size: 1080p source: HDTV video_codec: H.264 other: Reencoded release_group: GROUP type: episode # DDP is DD+ ? Show.Name.2016.S01E01.2160p.AMZN.WEBRip.DDP5.1.x264-Group : title: Show Name year: 2016 season: 1 episode: 1 screen_size: 2160p streaming_service: Amazon Prime source: Web other: Rip audio_codec: Dolby Digital Plus audio_channels: '5.1' video_codec: H.264 release_group: Group type: episode ? Show Name S02e19 [Mux - H264 - Ita Aac] DLMux by UBi : title: Show Name season: 2 episode: 19 video_codec: H.264 language: it audio_codec: AAC source: Web other: Mux release_group: UBi type: episode ? Show Name S01e10[Mux - 1080p - H264 - Ita Eng Ac3 - Sub Ita Eng]DLMux By GiuseppeTnT Littlelinx : title: Show Name season: 1 episode: 10 screen_size: 1080p video_codec: H.264 language: [it, en] source: Web other: Mux audio_codec: Dolby Digital subtitle_language: [it, en] release_group: GiuseppeTnT Littlelinx type: episode ? Show Name S04e07-08 [H264 - Ita Aac] HDTVMux by Group : title: Show Name season: 4 episode: [7, 8] video_codec: H.264 language: it audio_codec: AAC source: HDTV other: Mux release_group: Group type: episode ? Show Name 3x18 Un Tuffo Nel Passato ITA HDTVMux x264 Group : title: Show Name season: 3 episode: 18 episode_title: Un Tuffo Nel Passato language: it source: HDTV other: Mux video_codec: H.264 release_group: Group type: episode ? Show.Name.S03.1080p.BlurayMUX.AVC.DTS-HD.MA : title: Show Name season: 3 screen_size: 1080p source: Blu-ray other: Mux video_codec: H.264 audio_codec: DTS-HD audio_profile: Master Audio type: episode ? Show.Name.-.07.(2016).[RH].[English.Dubbed][WEBRip]..[HD.1080p] : options: -t episode episode: 7 source: Web other: Rip language: en other: [HD, Rip] screen_size: 1080p title: Show Name type: episode year: 2016 ? Show.Name.-.476-479.(2007).[HorribleSubs][WEBRip]..[HD.720p] : options: -t episode episode: - 476 - 477 - 478 - 479 source: Web other: [Rip, HD] release_group: HorribleSubs screen_size: 720p title: Show Name type: episode year: 2007 ? /11.22.63/Season 1/11.22.63.106.hdtv-abc : options: -T 11.22.63 title: 11 22 63 season: 1 episode: 6 source: HDTV release_group: abc type: episode ? Proof.2015.S01E10.1080p.WEB-DL.DD5.1.H.264-KINGS.mkv : title: Proof season: 1 episode: 10 screen_size: 1080p source: Web audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: KINGS container: mkv type: episode # Hardcoded subtitles ? Show.Name.S06E16.HC.SWESUB.HDTV.x264 : title: Show Name season: 6 episode: 16 other: Hardcoded Subtitles source: HDTV video_codec: H.264 subtitle_language: sv type: episode ? From [ WWW.TORRENTING.COM ] - White.Rabbit.Project.S01E08.1080p.NF.WEBRip.DD5.1.x264-ViSUM/White.Rabbit.Project.S01E08.1080p.NF.WEBRip.DD5.1.x264-ViSUM.mkv : title: White Rabbit Project website: WWW.TORRENTING.COM season: 1 episode: 8 screen_size: 1080p streaming_service: Netflix source: Web other: Rip audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: ViSUM container: mkv type: episode ? /tv/Daniel Tiger's Neighborhood/S02E06 - Playtime Is Different.mp4 : season: 2 episode: 6 title: Daniel Tiger's Neighborhood episode_title: Playtime Is Different container: mp4 type: episode ? Zoo.S02E05.1080p.WEB-DL.DD5.1.H.264.HKD/160725_02.mkv : title: Zoo season: 2 episode: 5 screen_size: 1080p source: Web audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: HKD container: mkv type: episode ? We.Bare.Bears.S01E14.Brother.Up.1080p.WEB-DL.AAC2.0.H.264-TVSmash/mxNMuJWeO7PUWCMEwqKSsS6D8Vs9S6V3PHD.mkv : title: We Bare Bears season: 1 episode: 14 episode_title: Brother Up screen_size: 1080p source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: TVSmash container: mkv type: episode ? Beyond.S01E02.Tempus.Fugit.720p.FREE.WEBRip.AAC2.0.x264-BTW/gNWDXow11s7E0X7GTDrZ.mkv : title: Beyond season: 1 episode: 2 episode_title: Tempus Fugit screen_size: 720p source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: BTW container: mkv type: episode ? Bones.S12E02.The.Brain.In.The.Bot.1080p.WEB-DL.DD5.1.H.264-R2D2/161219_06.mkv : title: Bones season: 12 episode: 2 episode_title: The Brain In The Bot screen_size: 1080p source: Web audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: R2D2 container: mkv type: episode ? The.Messengers.2015.S01E07.1080p.WEB-DL.DD5.1.H264.Nlsubs-Q/QoQ-sbuSLN.462.H.1.5DD.LD-BEW.p0801.70E10S.5102.sregnesseM.ehT.mkv : title: The Messengers year: 2015 season: 1 episode: 7 screen_size: 1080p source: Web audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 subtitle_language: nl release_group: Q container: mkv type: episode ? /Finding.Carter.S02E01.Love.the.Way.You.Lie.1080p.WEB-DL.AAC2.0.H.264-NL/LN-462.H.0.2CAA.LD-BEW.p0801.eiL.uoY.yaW.eht.evoL.10E20S.retraC.gnidniF.mkv : title: Finding Carter season: 2 episode: 1 episode_title: Love the Way You Lie screen_size: 1080p source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: NL container: mkv type: episode ? Mr.Robot.S02E12.1080p.WEB-DL.DD5.1-NL.Subs-Het.Robot.Team.OYM/sbuS LN-1.5DD LD-BEW p0801 21E20S toboR .rM.mkv : title: Mr Robot season: 2 episode: 12 screen_size: 1080p source: Web audio_codec: Dolby Digital audio_channels: '5.1' release_group: Het.Robot.Team.OYM type: episode ? Show.Name.-.Temporada.1.720p.HDTV.x264[Cap.102]SPANISH.AUDIO-NEWPCT ? /Show Name/Season 01/Show.Name.-.Temporada.1.720p.HDTV.x264[Cap.102]SPANISH.AUDIO-NEWPCT ? /Show Name/Temporada 01/Show.Name.-.Temporada.1.720p.HDTV.x264[Cap.102]SPANISH.AUDIO-NEWPCT : title: Show Name season: 1 episode: 2 screen_size: 720p source: HDTV video_codec: H.264 language: es release_group: NEWPCT type: episode # newpct ? Show Name - Temporada 4 [HDTV][Cap.408][Espanol Castellano] ? Show Name - Temporada 4 [HDTV][Cap.408][Español Castellano] : title: Show Name season: 4 episode: 8 source: HDTV language: ca type: episode # newpct ? -Show Name - Temporada 4 [HDTV][Cap.408][Espanol Castellano] ? -Show Name - Temporada 4 [HDTV][Cap.408][Español Castellano] : release_group: Castellano # newpct ? Show.Name.-.Temporada1.[HDTV][Cap.105][Español.Castellano] : title: Show Name source: HDTV season: 1 episode: 5 language: ca type: episode # newpct ? Show.Name.-.Temporada1.[HDTV][Cap.105][Español] : title: Show Name source: HDTV season: 1 episode: 5 language: es type: episode # newpct - season and episode with range: ? Show.Name.-.Temporada.1.720p.HDTV.x264[Cap.102_104]SPANISH.AUDIO-NEWPCT : title: Show Name season: 1 episode: [2, 3, 4] screen_size: 720p source: HDTV video_codec: H.264 language: es release_group: NEWPCT type: episode # newpct - season and episode (2 digit season) ? Show.Name.-.Temporada.15.720p.HDTV.x264[Cap.1503]SPANISH.AUDIO-NEWPCT : title: Show Name season: 15 episode: 3 screen_size: 720p source: HDTV video_codec: H.264 language: es release_group: NEWPCT type: episode # newpct - season and episode (2 digit season with range) ? Show.Name.-.Temporada.15.720p.HDTV.x264[Cap.1503_1506]SPANISH.AUDIO-NEWPCT : title: Show Name season: 15 episode: [3, 4, 5, 6] screen_size: 720p source: HDTV video_codec: H.264 language: es release_group: NEWPCT type: episode # newpct - season and episode: ? Show.Name.-.Temp.1.720p.HDTV.x264[Cap.102]SPANISH.AUDIO-NEWPCT : title: Show Name season: 1 episode: 2 screen_size: 720p source: HDTV video_codec: H.264 language: es release_group: NEWPCT type: episode # newpct - season and episode: ? Show.Name.-.Tem.1.720p.HDTV.x264[Cap.102]SPANISH.AUDIO-NEWPCT : title: Show Name season: 1 episode: 2 screen_size: 720p source: HDTV video_codec: H.264 language: es release_group: NEWPCT type: episode # newpct - season and episode: ? Show.Name.-.Tem.1.720p.HDTV.x264[Cap.112_114.Final]SPANISH.AUDIO-NEWPCT : title: Show Name season: 1 episode: [12, 13, 14] screen_size: 720p source: HDTV video_codec: H.264 language: es release_group: NEWPCT episode_details: Final type: episode ? Mastercook Italia - Stagione 6 (2016) 720p ep13 spyro.mkv : title: Mastercook Italia season: 6 episode: 13 year: 2016 screen_size: 720p episode_title: spyro container: mkv type: episode ? Mastercook Italia - Stagione 6 (2016) 720p Episodio 13 spyro.mkv : title: Mastercook Italia season: 6 year: 2016 screen_size: 720p episode: 13 episode_title: spyro container: mkv type: episode # Italian releases ? Show Name 3x18 Un Tuffo Nel Passato ITA HDTVMux x264 NovaRip : title: Show Name season: 3 episode: 18 episode_title: Un Tuffo Nel Passato language: it source: HDTV other: Mux video_codec: H.264 release_group: NovaRip type: episode # Italian releases ? Show Name 3x18 Un Tuffo Nel Passato ITA HDTVMux x264 NovaRip : title: Show Name season: 3 episode: 18 episode_title: Un Tuffo Nel Passato language: it source: HDTV other: Mux video_codec: H.264 release_group: NovaRip type: episode # Subbed: No language hint ? Show.Name.S06E03.1080p.HDTV.Legendado : subtitle_language: und # Subbed: No language hint ? Show.Name.S01E09.Subbed.1080p.BluRay.x264-RRH : title: Show Name season: 1 episode: 9 subtitle_language: und screen_size: 1080p source: Blu-ray video_codec: H.264 release_group: RRH type: episode # Legendado PT-BR ? Show.Name.S06E05.1080p.WEBRip.Legendado.PT-BR ? Show.Name.S06E05.1080p.WEBRip.Legendas.PT-BR ? Show.Name.S06E05.1080p.WEBRip.Legenda.PT-BR : title: Show Name season: 6 episode: 5 screen_size: 1080p source: Web other: Rip subtitle_language: pt-BR type: episode ? Show.Name.S01E07.Super, Title.WEB-DL 720p.br.srt : title: Show Name season: 1 episode: 7 episode_title: Super, Title source: Web screen_size: 720p subtitle_language: pt-BR container: srt type: episode ? -Show.Name.S01E07.Super, Title.WEB-DL 720p.br.srt : language: pt-BR # Legendado PT ? Show.Name.S06E05.1080p.WEBRip.Legendado.PT : title: Show Name season: 6 episode: 5 screen_size: 1080p source: Web other: Rip subtitle_language: pt type: episode ? Show.Name.S05E01.SPANISH.SUBBED.720p.HDTV.x264-sPHD : title: Show Name season: 5 episode: 1 subtitle_language: spa screen_size: 720p source: HDTV video_codec: H.264 release_group: sPHD type: episode ? Show.Name.S01E01.German.Subbed.HDTV.XviD-ASAP : title: Show Name season: 1 episode: 1 subtitle_language: deu source: HDTV video_codec: Xvid release_group: ASAP type: episode ? Show.Name.S04E21.Aint.Nothing.Like.the.Real.Thing.German.Custom.Subbed.720p.HDTV.x264.iNTERNAL-BaCKToRG : title: Show Name season: 4 episode: 21 episode_title: Aint Nothing Like the Real Thing subtitle_language: deu screen_size: 720p source: HDTV video_codec: H.264 type: episode ? Show.Name.S01.Season.Complet.WEBRiP.Ro.Subbed.TM : title: Show Name season: 1 other: [Complete, Rip] source: Web subtitle_language: ro type: episode ? Show.Name.(2013).Season.3.-.Eng.Soft.Subtitles.720p.WEBRip.x264.[MKV,AC3,5.1].Ehhhh : title: Show Name year: 2013 season: 3 subtitle_language: en screen_size: 720p source: Web other: Rip video_codec: H.264 container: mkv audio_codec: Dolby Digital audio_channels: '5.1' release_group: Ehhhh type: episode # Dublado ? Show.Name.S02E03.720p.HDTV.x264-Belex.-.Dual.Audio.-.Dublado : title: Show Name season: 2 episode: 3 screen_size: 720p source: HDTV video_codec: H.264 release_group: Belex other: Dual Audio language: und type: episode ? Show.Name.S06E10.1080p.WEB-DL.DUAL.[Dublado].RK : title: Show Name season: 6 episode: 10 screen_size: 1080p source: Web other: Dual Audio language: und release_group: RK type: episode ? Show.Name.S06E12.720p.WEB-DL.Dual.Audio.Dublado : title: Show Name season: 6 episode: 12 screen_size: 720p source: Web other: Dual Audio language: und type: episode ? Show.Name.S05E07.720p.DUBLADO.HDTV.x264-0SEC-pia.mkv : title: Show Name season: 5 episode: 7 screen_size: 720p language: und source: HDTV video_codec: H.264 release_group: 0SEC-pia container: mkv type: episode ? Show.Name.S02E07.Shiva.AC3.Dubbed.WEBRip.x264 : title: Show Name season: 2 episode: 7 episode_title: Shiva audio_codec: Dolby Digital language: und source: Web other: Rip video_codec: H.264 type: episode # Legendas ? Show.Name.S05.1080p.BluRay.x264-Belex.-.Dual.Audio.+.Legendas : title: Show Name season: 5 screen_size: 1080p source: Blu-ray video_codec: H.264 release_group: Belex other: Dual Audio subtitle_language: und type: episode # Legendas ? Show.Name.S05.1080p.BluRay.x264-Belex.-.Dual.Audio.+.Legendas : title: Show Name season: 5 screen_size: 1080p source: Blu-ray video_codec: H.264 release_group: Belex other: Dual Audio subtitle_language: und type: episode # Subtitulado ? Show.Name.S01E03.HDTV.Subtitulado.Esp.SC ? Show.Name.S01E03.HDTV.Subtitulado.Espanol.SC ? Show.Name.S01E03.HDTV.Subtitulado.Español.SC : title: Show Name season: 1 episode: 3 source: HDTV subtitle_language: es release_group: SC type: episode # Subtitles/Subbed ? Show.Name.S02E08.720p.WEB-DL.Subtitles ? Show.Name.S02E08.Subbed.720p.WEB-DL : title: Show Name season: 2 episode: 8 screen_size: 720p source: Web subtitle_language: und type: episode # Dubbed ? Show.Name.s01e01.german.Dubbed : title: Show Name season: 1 episode: 1 language: de type: episode ? Show.Name.S06E05.Das.Toor.German.AC3.Dubbed.HDTV.German : title: Show Name season: 6 episode: 5 language: de audio_codec: Dolby Digital source: HDTV type: episode ? Show.Name.S01E01.Savage.Season.GERMAN.DUBBED.WS.HDTVRip.x264-TVP : title: Show Name season: 1 episode: 1 episode_title: Savage Season language: de other: [Widescreen, Rip] source: HDTV video_codec: H.264 release_group: TVP type: episode # Dubbed ? "[AnimeRG].Show.Name.-.03.[Eng.Dubbed].[720p].[WEB-DL].[JRR]" : title: Show Name episode: 3 language: en screen_size: 720p source: Web release_group: JRR type: episode # Dubbed ? "[RH].Show.Name.-.03.[English.Dubbed].[1080p]" : title: Show Name episode: 3 language: en screen_size: 1080p release_group: RH type: episode # Hebsubs ? Show.Name.S05E05.HDTV.XviD-AFG.HebSubs : title: Show Name season: 5 episode: 5 source: HDTV video_codec: Xvid release_group: AFG subtitle_language: he type: episode ? Show Name - S02E31 - Episode 55 (720p.HDTV) : title: Show Name season: 2 episode: 31 episode_title: Episode 55 screen_size: 720p source: HDTV type: episode # Scenario: Removing invalid season and episode matches. Correct episode_title match ? Show.Name.S02E06.eps2.4.m4ster-s1ave.aes.1080p.AMZN.WEBRip.DD5.1.x264-GROUP : title: Show Name season: 2 episode: 6 episode_title: eps2 4 m4ster-s1ave aes screen_size: 1080p streaming_service: Amazon Prime source: Web other: Rip audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: GROUP type: episode ? Show.Name.S01E05.3xpl0its.wmv.720p.WEBdl.EN-SUB.x264-[MULVAcoded].mkv : title: Show Name season: 1 episode: 5 episode_title: 3xpl0its screen_size: 720p source: Web subtitle_language: en video_codec: H.264 type: episode # Regression: S4L release group detected as season 4 # https://github.com/guessit-io/guessit/issues/352 ? Show Name S01E06 DVD-RIP x264-S4L : title: Show Name season: 1 episode: 6 source: DVD video_codec: H.264 release_group: S4L type: episode # Corner case with only date and 720p ? The.Show.Name.2016.05.18.720.HDTV.x264-GROUP.VTV : title: The Show Name date: 2016-05-18 screen_size: 720p source: HDTV video_codec: H.264 release_group: GROUP.VTV type: episode # Corner case with only date and 720p ? -The.Show.Name.2016.05.18.720.HDTV.x264-GROUP.VTV : season: 7 episode: 20 # https://github.com/guessit-io/guessit/issues/308 (conflict with screen size) ? "[SuperGroup].Show.Name.-.06.[720.Hi10p][1F5578AC]" : title: Show Name episode: 6 screen_size: 720p color_depth: 10-bit crc32: 1F5578AC release_group: SuperGroup type: episode # https://github.com/guessit-io/guessit/issues/308 (conflict with screen size) ? "[SuperGroup].Show.Name.-.06.[1080.Hi10p][1F5578AC]" : title: Show Name episode: 6 screen_size: 1080p color_depth: 10-bit crc32: 1F5578AC release_group: SuperGroup type: episode ? "[MK-Pn8].Dimension.W.-.05.[720p][Hi10][Dual][TV-Dub][EDA6E7F1]" : options: -C us -L und release_group: MK-Pn8 title: Dimension W episode: 5 screen_size: 720p color_depth: 10-bit other: Dual Audio source: TV language: und crc32: EDA6E7F1 type: episode ? "[Zero-Raws].Show.Name.493-498.&.500-507.(CX.1280x720.VFR.x264.AAC)" : release_group: Zero-Raws title: Show Name episode: [493, 494, 495, 496, 497, 498, 500, 501, 502, 503, 504, 505, 506, 507] screen_size: 720p other: Variable Frame Rate video_codec: H.264 audio_codec: AAC type: episode # NetflixUHD ? Show.Name.S01E06.NetflixUHD : title: Show Name season: 1 episode: 6 streaming_service: Netflix other: Ultra HD type: episode ? Show.Name.S04E13.FINAL.MULTI.DD51.2160p.NetflixUHDRip.x265-TVS : title: Show Name season: 4 episode: 13 episode_details: Final language: mul audio_codec: Dolby Digital audio_channels: '5.1' screen_size: 2160p streaming_service: Netflix source: Ultra HDTV other: Rip video_codec: H.265 release_group: TVS type: episode ? Show.Name.S06E11.Of.Late.I.Think.of.Rosewood.iTunesHD.x264 : title: Show Name season: 6 episode: 11 episode_title: Of Late I Think of Rosewood streaming_service: iTunes other: HD video_codec: H.264 type: episode ? Show.Name.S01.720p.iTunes.h264-Group : title: Show Name season: 1 screen_size: 720p streaming_service: iTunes video_codec: H.264 release_group: Group type: episode ? Show.Name.1x01.eps1.0.hellofriend.(HDiTunes.Ac3.Esp).(2015).By.Malaguita.avi : title: Show Name season: 1 episode: 1 episode_title: eps1 0 hellofriend other: HD streaming_service: iTunes audio_codec: Dolby Digital language: spa year: 2015 container: avi type: episode ? "[Hanamaru&LoliHouse] The Dragon Dentist - 01 [WebRip 1920x1080 HEVC-yuv420p10 AAC].mkv" : release_group: Hanamaru&LoliHouse title: The Dragon Dentist episode: 1 source: Web other: Rip screen_size: 1080p video_codec: H.265 color_depth: 10-bit audio_codec: AAC container: mkv type: episode ? Show Name - Season 1 Episode 50 : title: Show Name season: 1 episode: 50 type: episode ? Vikings.Seizoen.4.1080p.Web.NLsubs : title: Vikings season: 4 screen_size: 1080p source: Web subtitle_language: nl type: episode ? Star.Wars.Rebels.S01E01.Spark.of.Rebellion.ALTERNATE.CUT.HDTV.x264-W4F.mp4 : title: Star Wars Rebels season: 1 episode: 1 episode_title: Spark of Rebellion edition: Alternative Cut source: HDTV video_codec: H.264 release_group: W4F container: mp4 type: episode ? DCs.Legends.of.Tomorrow.S02E12.HDTV.XviD-FUM : title: DCs Legends of Tomorrow season: 2 episode: 12 source: HDTV video_codec: Xvid release_group: FUM type: episode ? DC's Legends of Tomorrow 2016 - S02E02 : title: DC's Legends of Tomorrow year: 2016 season: 2 episode: 2 type: episode ? Broadchurch.S01.DIRFIX.720p.BluRay.x264-SHORTBREHD : title: Broadchurch season: 1 other: Fix screen_size: 720p source: Blu-ray video_codec: H.264 release_group: SHORTBREHD -proper_count: 1 type: episode ? Simply Red - 2016-07-08 Montreux Jazz Festival 720p : title: Simply Red date: 2016-07-08 episode_title: Montreux Jazz Festival screen_size: 720p type: episode ? Ridiculousness.S07E14.iNTERNAL.HDTV.x264-YesTV : title: Ridiculousness season: 7 episode: 14 other: Internal source: HDTV video_codec: H.264 release_group: YesTV type: episode ? Stephen.Colbert.2016.05.25.James.McAvoy.iNTERNAL.XviD-AFG : title: Stephen Colbert date: 2016-05-25 episode_title: James McAvoy other: Internal video_codec: Xvid release_group: AFG type: episode ? The.100.S01E13.iNTERNAL.READNFO.720p.HDTV.x264-2HD : title: The 100 season: 1 episode: 13 other: [Internal, Read NFO] screen_size: 720p source: HDTV video_codec: H.264 release_group: 2HD type: episode ? The.100.S01E13.READ.NFO.720p.HDTV.x264-2HD : title: The 100 season: 1 episode: 13 other: Read NFO screen_size: 720p source: HDTV video_codec: H.264 release_group: 2HD type: episode ? Dr.Ken.S01E21.SAMPLEFIX.720p.HDTV.x264-SVA : title: Dr Ken season: 1 episode: 21 other: Fix screen_size: 720p source: HDTV video_codec: H.264 release_group: SVA type: episode ? Rick and Morty Season 1 [UNCENSORED] [BDRip] [1080p] [HEVC] : title: Rick and Morty season: 1 edition: Uncensored other: Rip source: Blu-ray screen_size: 1080p video_codec: H.265 type: episode ? 12.Monkeys.S01E01.LiMiTED.FRENCH.1080p.WEB-DL.H264-AUTHORiTY : title: 12 Monkeys season: 1 episode: 1 edition: Limited language: french screen_size: 1080p source: Web video_codec: H.264 release_group: AUTHORiTY type: episode ? Undateable.2014.S03E05.West.Feed.HDTV.x264-2HD : title: Undateable year: 2014 season: 3 episode: 5 other: West Coast Feed source: HDTV video_codec: H.264 release_group: 2HD type: episode ? Undateable.2014.S02E07-E08.Live.Episode.West.Coast.Feed.HDTV.x264-2HD : title: Undateable year: 2014 season: 2 episode: [7, 8] other: West Coast Feed source: HDTV video_codec: H.264 release_group: 2HD type: episode ? Undateable.S03E01-E02.LIVE.EAST.FEED.720p.HDTV.x264-KILLERS : title: Undateable season: 3 episode: [1, 2] other: East Coast Feed screen_size: 720p source: HDTV video_codec: H.264 release_group: KILLERS type: episode ? Undateable.2014.S02E07.Live.Episode.East.Coast.Feed.HDTV.x264-2HD : title: Undateable year: 2014 season: 2 episode: 7 other: East Coast Feed source: HDTV video_codec: H.264 release_group: 2HD type: episode ? Undateable.2014.S02E07.East.Coast.Feed.720p.WEB-DL.DD5.1.H.264-NTb : title: Undateable year: 2014 season: 2 episode: 7 other: East Coast Feed screen_size: 720p source: Web audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: NTb type: episode ? True Detective S02E04 720p HDTV x264-0SEC [GloDLS].mkv : title: True Detective season: 2 episode: 4 screen_size: 720p source: HDTV video_codec: H.264 release_group: 0SEC [GloDLS] container: mkv type: episode ? Anthony.Bourdain.Parts.Unknown.S09E01.Los.Angeles.720p.HDTV.x264-MiNDTHEGAP : title: Anthony Bourdain Parts Unknown season: 9 episode: 1 episode_title: Los Angeles screen_size: 720p source: HDTV video_codec: H.264 release_group: MiNDTHEGAP type: episode ? -feud.s01e05.and.the.winner.is.(the.oscars.of.1963).720p.amzn.webrip.dd5.1.x264-casstudio.mkv : year: 1963 ? feud.s01e05.and.the.winner.is.(the.oscars.of.1963).720p.amzn.webrip.dd5.1.x264-casstudio.mkv : title: feud season: 1 episode: 5 episode_title: and the winner is screen_size: 720p streaming_service: Amazon Prime source: Web other: Rip audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: casstudio container: mkv type: episode ? Adventure.Time.S08E16.Elements.Part.1.Skyhooks.720p.WEB-DL.AAC2.0.H.264-RTN.mkv : title: Adventure Time season: 8 episode: 16 episode_title: Elements Part 1 Skyhooks screen_size: 720p source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: RTN container: mkv type: episode ? D:\TV\SITCOMS (CLASSIC)\That '70s Show\Season 07\That '70s Show - S07E22 - 2000 Light Years from Home.mkv : title: That '70s Show season: 7 episode: 22 episode_title: 2000 Light Years from Home container: mkv type: episode ? Show.Name.S02E01.Super.Title.720p.WEB-DL.DD5.1.H.264-ABC.nzb : title: Show Name season: 2 episode: 1 episode_title: Super Title screen_size: 720p source: Web audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: ABC container: nzb type: episode ? "[SGKK] Bleach 312v1 [720p/mkv]-Group.mkv" : title: Bleach episode: 312 version: 1 screen_size: 720p release_group: Group container: mkv type: episode ? The.Expanse.S02E08.720p.WEBRip.x264.EAC3-KiNGS.mkv : title: The Expanse season: 2 episode: 8 screen_size: 720p source: Web other: Rip video_codec: H.264 audio_codec: Dolby Digital Plus release_group: KiNGS container: mkv type: episode ? Series_name.2005.211.episode.title.avi : title: Series name year: 2005 season: 2 episode: 11 episode_title: episode title container: avi type: episode ? the.flash.2014.208.hdtv-lol[ettv].mkv : title: the flash year: 2014 season: 2 episode: 8 source: HDTV release_group: lol[ettv] container: mkv type: episode ? "[Despair-Paradise].Kono.Subarashii.Sekai.ni.Shukufuku.wo!.2.-..09.vostfr.FHD" : release_group: Despair-Paradise title: Kono Subarashii Sekai ni Shukufuku wo! 2 episode: 9 subtitle_language: fr other: Full HD type: episode ? Whose Line is it anyway/Season 01/Whose.Line.is.it.Anyway.US.S13E01.720p.WEB.x264-TBS.mkv : title: Whose Line is it Anyway season: 13 episode: 1 country: US screen_size: 720p source: Web video_codec: H.264 release_group: TBS container: mkv type: episode ? Planet.Earth.II.S01.2160p.UHD.BluRay.HDR.DTS-HD.MA5.1.x265-ULTRAHDCLUB : title: Planet Earth II season: 1 screen_size: 2160p source: Ultra HD Blu-ray other: HDR10 audio_codec: DTS-HD audio_profile: Master Audio audio_channels: '5.1' video_codec: H.265 release_group: ULTRAHDCLUB type: episode ? Reizen.Waes.S03.FLEMISH.1080p.HDTV.MP2.H.264-NOGRP/Reizen.Waes.S03E05.China.PART1.FLEMISH.1080p.HDTV.MP2.H.264-NOGRP.mkv : title: Reizen Waes season: 3 episode: 5 part: 1 language: nl-BE screen_size: 1080p source: HDTV video_codec: H.264 audio_codec: MP2 release_group: NOGRP container: mkv type: episode ? "/folder/Marvels.Agent.Carter.S02E05.The.Atomic.Job.1080p.WEB-DL.DD5.1.H264-Coo7[rartv]/Marvel's.Agent.Carter.S02E05.The.Atomic.Job.1080p.WEB-DL.DD5.1.H.264-Coo7.mkv" : title: Marvel's Agent Carter season: 2 episode: 5 episode_title: The Atomic Job release_group: Coo7 type: episode ? My.Name.Is.Earl.S01-S04.DVDRip.XviD-AR : title: My Name Is Earl season: [1, 2, 3, 4] source: DVD other: Rip video_codec: Xvid release_group: AR type: episode ? American.Dad.S01E01.Pilot.DVDRip.x264-CS : title: American Dad season: 1 episode: 1 episode_details: Pilot source: DVD other: Rip video_codec: H.264 release_group: CS type: episode ? Black.Sails.S01E01.HDTV.XviD.HebSubs-DR : title: Black Sails season: 1 episode: 1 source: HDTV video_codec: Xvid subtitle_language: he release_group: DR type: episode ? The.West.Wing.S04E06.Game.On.720p.WEB-DL.AAC2.0.H.264-MC : title: The West Wing season: 4 episode: 6 episode_title: Game On screen_size: 720p source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: MC type: episode ? 12.Monkeys.S02E05.1080p.WEB-DL.DD5.1.H.264-NA : title: 12 Monkeys season: 2 episode: 5 screen_size: 1080p source: Web audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: NA type: episode ? Fear.the.Walking.Dead.S03E07.1080p.AMZN.WEBRip.DD5.1.x264-VLAD[rarbg]/Fear.the.Walking.Dead.S03E07.1080p.AMZN.WEB-DL.DD+5.1.H.264-VLAD.mkv : title: Fear the Walking Dead season: 3 episode: 7 screen_size: 1080p source: Web audio_codec: Dolby Digital Plus audio_channels: '5.1' video_codec: H.264 release_group: VLAD container: mkv type: episode ? American.Crime.S01E02.1080p.WEB-DL.DD5.1.H.264-NL : title: American Crime season: 1 episode: 2 screen_size: 1080p source: Web audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: NL type: episode ? Better.Call.Saul.S02.720p.HDTV.x264-TL : title: Better Call Saul season: 2 screen_size: 720p source: HDTV video_codec: H.264 release_group: TL type: episode ? 60.Minutes.2008.12.14.HDTV.XviD-YT : options: -T '60 Minutes' title: 60 Minutes date: 2008-12-14 source: HDTV video_codec: Xvid release_group: YT type: episode ? Storm.Chasers.Season.1 : title: Storm Chasers season: 1 type: episode ? Faking.It.2014.S03E08.720p.HDTV.x264-AVS : title: Faking It year: 2014 season: 3 episode: 8 screen_size: 720p source: HDTV video_codec: H.264 release_group: AVS type: episode ? /series/Marvel's Agents of S.H.I.E.L.D/Season 4/Marvels.Agents.of.S.H.I.E.L.D.S04E01.The.Ghost.1080p.WEB-DL.DD5.1.H.264-AG.mkv : title: Marvels Agents of S.H.I.E.L.D. season: 4 episode: 1 episode_title: The Ghost screen_size: 1080p source: Web audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: AG container: mkv type: episode ? "[FASubs & TTF] Inuyasha - 099 [DVD] [B15AA1AC].mkv" : release_group: FASubs & TTF title: Inuyasha episode: 99 source: DVD crc32: B15AA1AC container: mkv type: episode ? Show.Name.S01E03.PL.SUBBED.480p.WEBRiP.x264 : title: Show Name season: 1 episode: 3 subtitle_language: pl screen_size: 480p source: Web other: Rip video_codec: H.264 type: episode ? Show.Name.s10e15(233).480p.BDRip-AVC.Ukr.hurtom : title: Show Name season: 10 episode: 15 screen_size: 480p source: Blu-ray other: Rip video_codec: H.264 language: uk release_group: hurtom type: episode ? Goof.Troop.1x24.Waste.Makes.Haste.720p.HDTV.x264.CZ-SDTV : title: Goof Troop season: 1 episode: 24 episode_title: Waste Makes Haste screen_size: 720p source: HDTV video_codec: H.264 language: cs release_group: SDTV type: episode ? Marvels.Daredevil.S02E11.German.DL.DUBBED.2160p.WebUHD.x264-UHDTV : title: Marvels Daredevil season: 2 episode: 11 language: [de, mul] screen_size: 2160p source: Web video_codec: H.264 release_group: UHDTV type: episode ? BBC The Story of China 1 of 6 - Ancestors CC HDTV x264 AC3 2.0 720p mkv : title: BBC The Story of China episode: 1 episode_count: 6 episode_title: Ancestors source: HDTV video_codec: H.264 audio_codec: Dolby Digital audio_channels: '2.0' screen_size: 720p container: mkv type: episode ? Duck.Dynasty.S09E04.Drone.Survivor.720p.AE.WEBRip.AAC2.0.H264-BTW[rartv] : title: Duck Dynasty season: 9 episode: 4 episode_title: Drone Survivor screen_size: 720p streaming_service: A&E source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: BTW[rartv] type: episode ? Mr.Selfridge.S04E03.720p.WEB-DL.AAC2.0.H264-MS[rartv] : title: Mr Selfridge season: 4 episode: 3 screen_size: 720p source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: MS[rartv] type: episode ? Second.Chance.S01E02.One.More.Notch.1080p.WEB-DL.DD5.1.H264-SC[rartv] : title: Second Chance season: 1 episode: 2 episode_title: One More Notch screen_size: 1080p source: Web audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: rartv type: episode ? Total.Divas.S05E01.720p.HDTV.AAC2.0.H.264-SC-SDH : title: Total Divas season: 5 episode: 1 screen_size: 720p source: HDTV audio_codec: AAC audio_channels: '2.0' video_codec: H.264 video_profile: Scalable Video Coding release_group: SDH type: episode ? Marvel's Jessica Jones (2015) s01e09 - AKA Sin Bin.mkv : title: Marvel's Jessica Jones season: 1 episode: 9 episode_title: AKA Sin Bin 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 ? Greys.Anatomy.S07D1.NTSC.DVDR-ToF : title: Greys Anatomy season: 7 disc: 1 other: NTSC source: DVD release_group: ToF type: episode ? Greys.Anatomy.S07D1.NTSC.DVDR-ToF : title: Greys Anatomy season: 7 disc: 1 other: NTSC source: DVD release_group: ToF type: episode ? Greys.Anatomy.S07D1-3&5.NTSC.DVDR-ToF : title: Greys Anatomy season: 7 disc: [1, 2, 3, 5] other: NTSC source: DVD release_group: ToF type: episode ? El.Principe.2014.S01D01.SPANiSH.COMPLETE.BLURAY-COJONUDO : title: El Principe year: 2014 season: 1 disc: 1 language: spa other: Complete source: Blu-ray release_group: COJONUDO type: episode ? The Simpsons - Season 2 Complete [DVDRIP VP7 KEGGERMAN : title: The Simpsons season: 2 other: [Complete, Rip] source: DVD video_codec: VP7 release_group: KEGGERMAN type: episode ? Barney & Friends_ Easy as ABC (Season 9_ Episode 15)_VP8_Vorbis_360p.webm : title: Barney & Friends Easy as ABC season: 9 episode: 15 video_codec: VP8 audio_codec: Vorbis screen_size: 360p container: webm type: episode ? Victoria.S01.1080p.BluRay.HEVC.DTSMA.LPCM.PGS-OZM : title: Victoria season: 1 screen_size: 1080p source: Blu-ray video_codec: H.265 audio_codec: [DTS-HD, LPCM] audio_profile: Master Audio # Does it worth to add subtitle_format? Such rare case # subtitle_format: PGS # release_group: OZM type: episode ? The.Prisoners.S01E03.1080p.DM.AAC2.0.x264-BTN : title: The Prisoners season: 1 episode: 3 screen_size: 1080p source: Digital Master audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: BTN type: episode ? Panorama.S2013E25.Broken.by.Battle.1080p.DM.AAC2.0.x264-BTN : title: Panorama season: 2013 episode: 25 episode_title: Broken by Battle screen_size: 1080p source: Digital Master audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: BTN type: episode ? Our.World.S2014E11.Chinas.Model.Army.720p.DM.AAC2.0.x264-BTN : title: Our World season: 2014 episode: 11 episode_title: Chinas Model Army screen_size: 720p source: Digital Master audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: BTN type: episode ? Storyville.S2016E08.My.Nazi.Legacy.1080p.DM.x264-BTN : title: Storyville season: 2016 episode: 8 episode_title: My Nazi Legacy screen_size: 1080p source: Digital Master video_codec: H.264 release_group: BTN type: episode ? Comedians.in.Cars.Getting.Coffee.S07E01.1080p.DM.FLAC2.0.x264-NTb : title: Comedians in Cars Getting Coffee season: 7 episode: 1 screen_size: 1080p source: Digital Master audio_codec: FLAC audio_channels: '2.0' video_codec: H.264 release_group: NTb type: episode ? "[SomeGroup-Fansub]_Show_Name_727_[VOSTFR][HD_1280x720]" : release_group: SomeGroup-Fansub title: Show Name episode: 727 subtitle_language: fr other: HD screen_size: 720p type: episode ? "[GROUP]Show_Name_726_[VOSTFR]_[V1]_[8bit]_[720p]_[2F7B3FA2]" : release_group: GROUP title: Show Name episode: 726 subtitle_language: fr version: 1 color_depth: 8-bit screen_size: 720p crc32: 2F7B3FA2 type: episode ? Show Name 445 VOSTFR par Fansub-Resistance (1280*720) - version MQ : title: Show Name episode: 445 subtitle_language: fr screen_size: 720p type: episode ? Anime Show Episode 159 v2 [VOSTFR][720p][AAC].mp4 : title: Anime Show episode: 159 version: 2 subtitle_language: fr screen_size: 720p audio_codec: AAC container: mp4 type: episode ? "[Group] Anime Super Episode 161 [VOSTFR][720p].mp4" : release_group: Group title: Anime Super episode: 161 subtitle_language: fr screen_size: 720p container: mp4 type: episode ? Anime Show Episode 59 v2 [VOSTFR][720p][AAC].mp4 : title: Anime Show episode: 59 version: 2 subtitle_language: fr screen_size: 720p audio_codec: AAC container: mp4 type: episode ? Show.Name.-.476-479.(2007).[HorribleSubs][WEBRip]..[HD.720p] : title: Show Name episode: [476, 477, 478, 479] year: 2007 release_group: HorribleSubs source: Web other: [Rip, HD] screen_size: 720p type: episode ? Show Name - 722 [HD_1280x720].mp4 : title: Show Name episode: 722 other: HD screen_size: 720p container: mp4 type: episode ? Show!.Name.2.-.10.(2016).[HorribleSubs][WEBRip]..[HD.720p] : title: Show! Name 2 episode: 10 year: 2016 release_group: HorribleSubs source: Web other: [Rip, HD] screen_size: 720p type: episode ? 'C:\folder\[GROUP]_An_Anime_Show_100_-_10_[1080p]_mkv' : options: -T 'An Anime Show 100' release_group: GROUP title: An Anime Show 100 episode: 10 screen_size: 1080p container: mkv type: episode ? "[Group].Show.Name!.Super!!.-.05.[720p][AAC].mp4" : release_group: Group title: Show Name! Super!! episode: 5 screen_size: 720p audio_codec: AAC container: mp4 type: episode ? "[GROUP].Mobile.Suit.Gundam.Unicorn.RE.0096.-.14.[720p].mkv" : options: -T 'Mobile Suit Gundam Unicorn RE 0096' release_group: GROUP title: Mobile Suit Gundam Unicorn RE 0096 episode: 14 screen_size: 720p container: mkv type: episode ? Show.Name.-.Other Name.-.02.(1280x720.HEVC.AAC) : title: Show Name alternative_title: Other Name episode: 2 screen_size: 720p video_codec: H.265 audio_codec: AAC type: episode ? "[GroupName].Show.Name.-.02.5.(Special).[BD.1080p]" : release_group: GroupName title: Show Name episode: 2 episode_details: Special screen_size: 1080p source: Blu-ray type: episode ? "[Group].Show.Name.2.The.Big.Show.-.11.[1080p]" : title: Show Name 2 The Big Show episode: 11 screen_size: 1080p type: episode ? "[SuperGroup].Show.Name.-.Still.Name.-.11.[1080p]" : release_group: SuperGroup title: Show Name alternative_title: Still Name episode: 11 screen_size: 1080p type: episode ? "[SuperGroup].Show.Name.-.462" : release_group: SuperGroup title: Show Name episode: 462 type: episode ? Show.Name.10.720p : title: Show Name episode: 10 screen_size: 720p type: episode ? "[Group].Show.Name.G2.-.19.[1080p]" : release_group: Group title: Show Name G2 episode: 19 screen_size: 1080p type: episode ? "[Group].Show.Name.S2.-.19.[1080p]" ? /Show.Name.S2/[Group].Show.Name.S2.-.19.[1080p] ? /Show Name S2/[Group].Show.Name.S2.-.19.[1080p] : options: -T 'Show Name S2' release_group: Group title: Show Name S2 episode: 19 screen_size: 1080p type: episode ? "[ABC]_Show_Name_001.mkv" : release_group: ABC title: Show Name episode: 1 container: mkv type: episode ? 003-005. Show Name - Ep Name.mkv : episode: [3, 4, 5] title: Show Name episode_title: Ep Name container: mkv type: episode ? 003. Show Name - Ep Name.mkv : episode: 3 title: Show Name episode_title: Ep Name container: mkv type: episode ? 165.Show Name.s08e014 : title: 165 Show Name season: 8 episode: 14 type: episode ? Show Name - 16x03-05 - 313-315 ? Show.Name.16x03-05.313-315-GROUP ? Show Name 16x03-05 313-315 ? Show Name - 313-315 - s16e03-05 ? Show.Name.313-315.s16e03-05 ? Show Name 313-315 s16e03-05 : title: Show Name absolute_episode: [313, 314, 315] season: 16 episode: [3, 4, 5] type: episode ? Show Name 13-16 : title: Show Name episode: [13, 14, 15, 16] type: episode ? Show Name 804 vostfr HD : options: --episode-prefer-number title: Show Name episode: 804 subtitle_language: fr other: HD type: episode ? "[Doki] Re Zero kara Hajimeru Isekai Seikatsu - 01 1920x1080 Hi10P BD FLAC [7F64383D].mkv" : release_group: Doki title: Re Zero kara Hajimeru Isekai Seikatsu episode: 1 screen_size: 1080p aspect_ratio: 1.778 video_profile: High 10 color_depth: 10-bit source: Blu-ray audio_codec: FLAC crc32: 7F64383D container: mkv type: episode ? Shark Tank (AU) - S02E01 - HDTV-720p.mkv : title: Shark Tank country: AU season: 2 episode: 1 source: HDTV screen_size: 720p container: mkv type: episode ? "[HorribleSubs] Garo - Vanishing Line - 01 [1080p].mkv" : release_group: HorribleSubs title: Garo alternative_title: Vanishing Line episode: 1 screen_size: 1080p container: mkv type: episode ? "[HorribleSubs] Yowamushi Pedal - Glory Line - 01 [1080p].mkv" : release_group: HorribleSubs title: Yowamushi Pedal alternative_title: Glory Line episode: 1 screen_size: 1080p container: mkv type: episode ? c:\Temp\autosubliminal\completed\2 Broke Girls\Season 01\2 Broke Girls - S01E01 - HDTV-720p Proper - x264 AC3 - IMMERSE - [2011-09-19].mkv : title: 2 Broke Girls season: 1 episode: 1 source: HDTV screen_size: 720p other: Proper video_codec: H.264 audio_codec: Dolby Digital release_group: IMMERSE date: 2011-09-19 container: mkv type: episode ? c:\Temp\postprocessing\Marvels.Agents.of.S.H.I.E.L.D.s01e02.0.8.4.720p.WEB.DL.mkv : title: Marvels Agents of S.H.I.E.L.D. season: 1 episode: 2 episode_title: 0.8.4. screen_size: 720p source: Web container: mkv type: episode ? Mind.Field.S02E06.The.Power.of.Suggestion.1440p.H264.WEBDL.Subtitles : title: Mind Field season: 2 episode: 6 episode_title: The Power of Suggestion screen_size: 1440p video_codec: H.264 source: Web subtitle_language: und type: episode ? The Power of Suggestion - Mind Field S2 (Ep 6) (1440p_24fps_H264-384kbit_AAC 6Ch).mp4 : title: The Power of Suggestion alternative_title: Mind Field season: 2 episode: 6 screen_size: 1440p frame_rate: 24fps video_codec: H.264 audio_bit_rate: 384Kbps audio_codec: AAC audio_channels: '5.1' container: mp4 type: episode ? Mind.Field.S02E06.The.Power.of.Suggestion.1440p.H264.WEBDL.Subtitles/The Power of Suggestion - Mind Field S2 (Ep 6) (1440p_24fps_H264-384kbit_AAC 6Ch).mp4 : season: 2 episode: 6 title: The Power of Suggestion alternative_title: Mind Field screen_size: 1440p frame_rate: 24fps video_codec: H.264 source: Web subtitle_language: und audio_bit_rate: 384Kbps audio_codec: AAC audio_channels: '5.1' container: mp4 type: episode ? Mind.Field.S02E06.The.Power.of.Suggestion.1440p.H264.WEBDL.Subtitles/The Power of Suggestion - Mind Field S2 (Ep 6) (English).srt : title: Mind Field season: 2 episode: 6 episode_title: The Power of Suggestion screen_size: 1440p video_codec: H.264 source: Web subtitle_language: en container: srt type: episode ? Mind.Field.S02E06.The.Power.of.Suggestion.1440p.H264.WEBDL.Subtitles/The Power of Suggestion - Mind Field S2 (Ep 6) (Korean).srt : title: Mind Field season: 2 episode: 6 episode_title: The Power of Suggestion screen_size: 1440p video_codec: H.264 source: Web subtitle_language: ko container: srt type: episode ? '[HorribleSubs] Overlord II - 01 [1080p] 19.1mbits - 120fps.mkv' : release_group: HorribleSubs title: Overlord II episode: 1 screen_size: 1080p video_bit_rate: 19.1Mbps frame_rate: 120fps container: mkv type: episode ? One Piece - 720 : title: One Piece season: 7 episode: 20 type: episode ? foobar.213.avi : options: -E title: foobar episode: 213 container: avi type: episode ? FooBar - 360 368p-Grp : options: -E title: FooBar episode: 360 screen_size: 368p release_group: Grp type: episode ? wwiis.most.daring.raids.s01e04.storming.mussolinis.island.1080p.web.h.264-edhd-sample.mkv : title: wwiis most daring raids season: 1 episode: 4 episode_title: storming mussolinis island screen_size: 1080p source: Web video_codec: H.264 release_group: edhd other: Sample container: mkv type: episode ? WWIIs.Most.Daring.Raids.S01E04.Storming.Mussolinis.Island.1080p.WEB.h264-EDHD/wwiis.most.daring.raids.s01e04.storming.mussolinis.island.1080p.web.h.264-edhd-sample.mkv : title: wwiis most daring raids season: 1 episode: 4 episode_title: Storming Mussolinis Island screen_size: 1080p source: Web video_codec: H.264 release_group: edhd other: Sample container: mkv type: episode ? dcs.legends.of.tomorrow.s02e01.1080p.bluray.x264-rovers.proof : title: dcs legends of tomorrow season: 2 episode: 1 screen_size: 1080p source: Blu-ray video_codec: H.264 release_group: rovers other: Proof type: episode ? dcs.legends.of.tomorrow.s02e01.720p.bluray.x264-demand.sample.mkv : title: dcs legends of tomorrow season: 2 episode: 1 screen_size: 720p source: Blu-ray video_codec: H.264 release_group: demand other: Sample container: mkv type: episode ? Season 06/e01.1080p.bluray.x264-wavey-obfuscated.mkv : season: 6 episode: 1 screen_size: 1080p source: Blu-ray video_codec: H.264 title: wavey other: Obfuscated container: mkv type: episode ? Hells.Kitchen.US.S17E08.1080p.HEVC.x265-MeGusta-Obfuscated/c48db7d2aeb040e8a920a9fd6effcbf4.mkv : title: Hells Kitchen country: US season: 17 episode: 8 screen_size: 1080p video_codec: H.265 release_group: MeGusta other: Obfuscated uuid: c48db7d2aeb040e8a920a9fd6effcbf4 container: mkv type: episode ? Blue.Bloods.S08E09.1080p.HEVC.x265-MeGusta-Obfuscated/afaae96ae7a140e0981ced2a79221751.mkv : title: Blue Bloods season: 8 episode: 9 screen_size: 1080p video_codec: H.265 release_group: MeGusta other: Obfuscated container: mkv type: episode ? MacGyver.2016.S02E09.CD-ROM.and.Hoagie.Foil.1080p.AMZN.WEBRip.DDP5.1.x264-NTb-Scrambled/c329b27187d44a94b4a25b21502db552.mkv : title: MacGyver year: 2016 season: 2 episode: 9 screen_size: 1080p streaming_service: Amazon Prime source: Web other: [Rip, Obfuscated] audio_codec: Dolby Digital Plus audio_channels: '5.1' video_codec: H.264 release_group: NTb uuid: c329b27187d44a94b4a25b21502db552 container: mkv type: episode ? The.Late.Late.Show.with.James.Corden.2017.11.27.Armie.Hammer.Juno.Temple.Charlie.Puth.1080p.AMZN.WEB-DL.DDP2.0.H.264-monkee-Scrambled/42e7e8a48eb7454aaebebcf49705ce41.mkv : title: The Late Late Show with James Corden date: 2017-11-27 episode_title: Armie Hammer Juno Temple Charlie Puth screen_size: 1080p streaming_service: Amazon Prime source: Web audio_codec: Dolby Digital Plus audio_channels: '2.0' video_codec: H.264 release_group: monkee other: Obfuscated uuid: 42e7e8a48eb7454aaebebcf49705ce41 container: mkv type: episode ? Educating Greater Manchester S01E07 720p HDTV x264-PLUTONiUM-AsRequested : title: Educating Greater Manchester season: 1 episode: 7 screen_size: 720p source: HDTV video_codec: H.264 release_group: PLUTONiUM other: Repost type: episode ? Im A Celebrity Get Me Out Of Here S17E14 HDTV x264-PLUTONiUM-xpost : title: Im A Celebrity Get Me Out Of Here season: 17 episode: 14 source: HDTV video_codec: H.264 release_group: PLUTONiUM other: Repost type: episode ? Tales S01E08 All I Need Method Man Featuring Mary J Blige 720p BET WEBRip AAC2 0 x264-RTN-xpost : title: Tales season: 1 episode: 8 episode_title: All I Need Method Man Featuring Mary J Blige screen_size: 720p source: Web other: [Rip, Repost] audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: RTN type: episode ? This is Us S01E11 Herzensangelegenheiten German DL WS DVDRip x264-CDP-xpost : options: --exclude country title: This is Us season: 1 episode: 11 episode_title: Herzensangelegenheiten language: - de - mul other: - Widescreen - Rip - Repost source: DVD video_codec: H.264 release_group: CDP type: episode ? The Girlfriend Experience S02E10 1080p WEB H264-STRiFE-postbot : title: The Girlfriend Experience season: 2 episode: 10 screen_size: 1080p source: Web video_codec: H.264 release_group: STRiFE other: Repost type: episode ? The.Girlfriend.Experience.S02E10.1080p.WEB.H264-STRiFE-postbot/90550c1adaf44c47b60d24f59603bb98.mkv : title: The Girlfriend Experience season: 2 episode: 10 screen_size: 1080p source: Web video_codec: H.264 release_group: STRiFE other: Repost uuid: 90550c1adaf44c47b60d24f59603bb98 container: mkv type: episode ? 24.S01E02.1080p.BluRay.REMUX.AVC.DD.2.0-EPSiLON-xpost/eb518eaf33f641a1a8c6e0973a67aec2.mkv : title: '24' season: 1 episode: 2 screen_size: 1080p source: Blu-ray other: [Remux, Repost] video_codec: H.264 audio_codec: Dolby Digital audio_channels: '2.0' release_group: EPSiLON uuid: eb518eaf33f641a1a8c6e0973a67aec2 container: mkv type: episode ? Educating.Greater.Manchester.S01E02.720p.HDTV.x264-PLUTONiUM-AsRequested/47fbcb2393aa4b5cbbb340d3173ca1a9.mkv : title: Educating Greater Manchester season: 1 episode: 2 screen_size: 720p source: HDTV video_codec: H.264 release_group: PLUTONiUM other: Repost uuid: 47fbcb2393aa4b5cbbb340d3173ca1a9 container: mkv type: episode ? Stranger.Things.S02E05.Chapter.Five.Dig.Dug.720p.NF.WEBRip.DD5.1.x264-PSYPHER-AsRequested-Obfuscated : title: Stranger Things season: 2 episode: 5 episode_title: Chapter Five Dig Dug screen_size: 720p streaming_service: Netflix source: Web other: [Rip, Repost, Obfuscated] audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: PSYPHER type: episode ? Show.Name.-.Season.1.3.4-.Mp4.1080p : title: Show Name season: [1, 3, 4] container: mp4 screen_size: 1080p type: episode ? Bones.S03.720p.HDTV.x264-SCENE : title: Bones season: 3 screen_size: 720p source: HDTV video_codec: H.264 release_group: SCENE type: episode ? shes.gotta.have.it.s01e08.720p.web.x264-strife.mkv : title: shes gotta have it season: 1 episode: 8 screen_size: 720p source: Web video_codec: H.264 release_group: strife type: episode ? DuckTales.2017.S01E10.The.Missing.Links.of.Moorshire.PDTV.H.264.MP2-KIDKAT : title: DuckTales year: 2017 season: 1 episode: 10 episode_title: The Missing Links of Moorshire source: Digital TV video_codec: H.264 audio_codec: MP2 release_group: KIDKAT type: episode ? Por Trece Razones - Temporada 2 [HDTV 720p][Cap.201][AC3 5.1 Castellano]/Por Trece Razones 2x01 [des202].mkv : title: Por Trece Razones season: 2 source: HDTV screen_size: 720p episode: 1 audio_codec: Dolby Digital audio_channels: '5.1' language: Catalan release_group: des202 container: mkv type: episode ? Cuerpo de Elite - Temporada 1 [HDTV 720p][Cap.113][AC3 5.1 Esp Castellano]\CuerpoDeElite720p_113_desca202.mkv : title: Cuerpo de Elite season: 1 source: HDTV screen_size: 720p episode: 13 audio_codec: Dolby Digital audio_channels: '5.1' language: - Spanish - Catalan container: mkv type: episode ? Show.Name.S01E01.St.Patricks.Day.1080p.mkv : title: Show Name season: 1 episode: 1 episode_title: St Patricks Day screen_size: 1080p container: mkv type: episode ? Show.Name.S01E01.St.Patricks.Day.1080p-grp.mkv : title: Show Name season: 1 episode: 1 episode_title: St Patricks Day screen_size: 1080p release_group: grp container: mkv type: episode ? Titans.2018.S01E09.Hank.And.Dawn.720p.DCU.WEB-DL.AAC2.0.H264-NTb : title: Titans year: 2018 season: 1 episode: 9 episode_title: Hank And Dawn screen_size: 720p streaming_service: DC Universe source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: NTb type: episode ? S.W.A.T.2017.S01E21.Treibjagd.German.Dubbed.DL.AmazonHD.x264-TVS : title: S.W.A.T. year: 2017 season: 1 episode: 21 episode_title: Treibjagd language: - German - Multi streaming_service: Amazon Prime other: HD video_codec: H.264 release_group: TVS type: episode ? S.W.A.T.2017.S01E16.READNFO.720p.HDTV.x264-KILLERS : title: S.W.A.T. year: 2017 season: 1 episode: 16 other: Read NFO screen_size: 720p source: HDTV video_codec: H.264 release_group: KILLERS type: episode ? /mnt/NAS/NoSubsTVShows/Babylon 5/Season 01/Ep. 02 - Soul Hunter : title: Babylon 5 season: 1 episode: 2 episode_title: Soul Hunter type: episode ? This.is.Us.S01E01.HDTV.x264-KILLERS.mkv : title: This is Us season: 1 episode: 1 source: HDTV video_codec: H.264 release_group: KILLERS container: mkv type: episode ? Videos/Office1080/The Office (US) (2005) Season 2 S02 + Extras (1080p AMZN WEB-DL x265 HEVC 10bit AAC 2.0 LION)/The Office (US) (2005) - S02E12 - The Injury (1080p AMZN WEB-DL x265 LION).mkv : title: The Office country: US year: 2005 season: 2 other: Extras screen_size: 1080p streaming_service: Amazon Prime source: Web video_codec: H.265 video_profile: High Efficiency Video Coding color_depth: 10-bit audio_codec: AAC audio_channels: '2.0' release_group: LION episode: 12 episode_title: The Injury container: mkv type: episode ? Thumping.Spike.2.E01.DF.WEBRip.720p-DRAMATV.mp4 : title: Thumping Spike 2 episode: 1 source: Web other: Rip screen_size: 720p streaming_service: DramaFever release_group: DRAMATV container: mp4 mimetype: video/mp4 type: episode ? About.Time.E01.1080p.VIKI.WEB-DL-BLUEBERRY.mp4 : title: About Time episode: 1 screen_size: 1080p streaming_service: Viki source: Web release_group: BLUEBERRY container: mp4 mimetype: video/mp4 type: episode ? Eyes.Of.Dawn.1991.E01.480p.MBCVOD.AAC.x264-NOGPR.mp4 : title: Eyes Of Dawn year: 1991 season: 1991 episode: 1 screen_size: 480p streaming_service: MBC audio_codec: AAC video_codec: H.264 release_group: NOGPR container: mp4 mimetype: video/mp4 type: episode ? "Seitokai Yakuindomo - 14 OAD [BDRip 1920x1080 x264 FLAC].mkv" : title: Seitokai Yakuindomo episode: 14 source: Blu-ray other: [Original Animation DVD, Rip] screen_size: 1080p aspect_ratio: 1.778 video_codec: H.264 audio_codec: FLAC container: mkv type: episode ? "[EveTaku] Kyouso Giga ONA v2 [540p][128BAC43].mkv" : release_group: EveTaku title: Kyouso Giga other: Original Net Animation version: 2 screen_size: 540p crc32: 128BAC43 container: mkv type: episode ? '[Erai-raws] Fumetsu no Anata e - 03 [720p][Multiple Subtitle].mkv' : release_group: Erai-raws title: Fumetsu no Anata e episode: 3 screen_size: 720p subtitle_language: mul container: mkv type: episode ? Mom.S06E08.Jell-O.Shots.and.the.Truth.About.Santa.1080p.AMZN.WEB-DL.DDP5.1.H.264-NTb.mkv : title: Mom season: 6 episode: 8 episode_title: Jell-O Shots and the Truth About Santa screen_size: 1080p streaming_service: Amazon Prime source: Web audio_codec: Dolby Digital Plus audio_channels: '5.1' video_codec: H.264 release_group: NTb container: mkv type: episode ? Archer.2009.S12E05.Shots.720p.HULU.WEB-DL.DDP5.1.H.264-NOGRP : title: Archer year: 2009 season: 12 episode: 5 episode_title: Shots screen_size: 720p streaming_service: Hulu source: Web audio_codec: Dolby Digital Plus audio_channels: '5.1' video_codec: H.264 release_group: NOGRP type: episode ? /mydatapool/mydata/Videos/Shows/C/Caprica (2008)/Season 1/Apotheosis_1920x1080.mp4 : title: Caprica year: 2008 season: 1 episode_title: Apotheosis screen_size: 1080p container: mp4 type: episode ? 4400.S01E01.1080p.WEB.H264-NOGRP : title: '4400' season: 1 episode: 1 screen_size: 1080p source: Web video_codec: H.264 release_group: NOGRP type: episode guessit-3.8.0/guessit/test/movies.yml000066400000000000000000001235361453655371700177260ustar00rootroot00000000000000? __default__ : type: movie ? Movies/Fear and Loathing in Las Vegas (1998)/Fear.and.Loathing.in.Las.Vegas.720p.HDDVD.DTS.x264-ESiR.mkv : title: Fear and Loathing in Las Vegas year: 1998 screen_size: 720p source: HD-DVD audio_codec: DTS video_codec: H.264 container: mkv release_group: ESiR ? Movies/El Dia de la Bestia (1995)/El.dia.de.la.bestia.DVDrip.Spanish.DivX.by.Artik[SEDG].avi : title: El Dia de la Bestia year: 1995 source: DVD other: Rip language: spanish video_codec: DivX release_group: Artik[SEDG] container: avi ? Movies/Dark City (1998)/Dark.City.(1998).DC.BDRip.720p.DTS.X264-CHD.mkv : title: Dark City year: 1998 source: Blu-ray other: Rip screen_size: 720p audio_codec: DTS video_codec: H.264 release_group: CHD ? Movies/Sin City (BluRay) (2005)/Sin.City.2005.BDRip.720p.x264.AC3-SEPTiC.mkv : title: Sin City year: 2005 source: Blu-ray other: Rip screen_size: 720p video_codec: H.264 audio_codec: Dolby Digital release_group: SEPTiC ? Movies/Borat (2006)/Borat.(2006).R5.PROPER.REPACK.DVDRip.XviD-PUKKA.avi : title: Borat year: 2006 proper_count: 2 source: DVD other: [ Region 5, Proper, Rip ] video_codec: Xvid release_group: PUKKA ? Enter.the.Void.2009.2in1.1080p.BluRay.DD5.1.x264-EbP.mkv : title: Enter the Void year: 2009 other: 2in1 screen_size: 1080p source: Blu-ray audio_codec: Dolby Digital video_codec: H.264 release_group: EbP ? "[XCT].Le.Prestige.(The.Prestige).DVDRip.[x264.HP.He-Aac.{Fr-Eng}.St{Fr-Eng}.Chaps].mkv" : title: Le Prestige source: DVD other: Rip video_codec: H.264 video_profile: High audio_codec: AAC audio_profile: High Efficiency language: [ french, english ] subtitle_language: [ french, english ] release_group: Chaps ? Battle Royale (2000)/Battle.Royale.(Batoru.Rowaiaru).(2000).(Special.Edition).CD1of2.DVDRiP.XviD-[ZeaL].avi : title: Battle Royale year: 2000 edition: Special cd: 1 cd_count: 2 source: DVD other: Rip video_codec: Xvid release_group: ZeaL ? Movies/Brazil (1985)/Brazil_Criterion_Edition_(1985).CD2.avi : title: Brazil edition: Criterion year: 1985 cd: 2 ? Movies/Picnic.at.Hanging.Rock.1975.Criterion.Collection.1080p.BluRay.x264.DTS-WiKi : title: Picnic at Hanging Rock edition: Criterion year: 1975 ? Movies/Persepolis (2007)/[XCT] Persepolis [H264+Aac-128(Fr-Eng)+ST(Fr-Eng)+Ind].mkv : title: Persepolis year: 2007 video_codec: H.264 audio_codec: AAC language: [ French, English ] subtitle_language: [ French, English ] release_group: Ind ? Movies/Toy Story (1995)/Toy Story [HDTV 720p English-Spanish].mkv : title: Toy Story year: 1995 source: HDTV screen_size: 720p language: [ english, spanish ] ? Movies/Office Space (1999)/Office.Space.[Dual-DVDRip].[Spanish-English].[XviD-AC3-AC3].[by.Oswald].avi : title: Office Space year: 1999 other: [Dual Audio, Rip] source: DVD language: [ english, spanish ] video_codec: Xvid audio_codec: Dolby Digital ? Movies/Wild Zero (2000)/Wild.Zero.DVDivX-EPiC.avi : title: Wild Zero year: 2000 video_codec: DivX release_group: EPiC ? movies/Baraka_Edition_Collector.avi : title: Baraka edition: Collector ? Movies/Blade Runner (1982)/Blade.Runner.(1982).(Director's.Cut).CD1.DVDRip.XviD.AC3-WAF.avi : title: Blade Runner year: 1982 edition: Director's Cut cd: 1 source: DVD other: Rip video_codec: Xvid audio_codec: Dolby Digital release_group: WAF ? movies/American.The.Bill.Hicks.Story.2009.DVDRip.XviD-EPiSODE.[UsaBit.com]/UsaBit.com_esd-americanbh.avi : title: American The Bill Hicks Story year: 2009 source: DVD other: Rip video_codec: Xvid release_group: EPiSODE website: UsaBit.com ? movies/Charlie.And.Boots.DVDRip.XviD-TheWretched/wthd-cab.avi : title: Charlie And Boots source: DVD other: Rip video_codec: Xvid release_group: TheWretched ? movies/Steig Larsson Millenium Trilogy (2009) BRrip 720 AAC x264/(1)The Girl With The Dragon Tattoo (2009) BRrip 720 AAC x264.mkv : title: The Girl With The Dragon Tattoo #film_title: Steig Larsson Millenium Trilogy #film: 1 year: 2009 source: Blu-ray other: [Reencoded, Rip] audio_codec: AAC video_codec: H.264 screen_size: 720p ? movies/Greenberg.REPACK.LiMiTED.DVDRip.XviD-ARROW/arw-repack-greenberg.dvdrip.xvid.avi : title: Greenberg source: DVD video_codec: Xvid release_group: ARROW other: [Proper, Rip] edition: Limited proper_count: 1 ? Movies/Fr - Paris 2054, Renaissance (2005) - De Christian Volckman - (Film Divx Science Fiction Fantastique Thriller Policier N&B).avi : title: Paris 2054, Renaissance year: 2005 language: french video_codec: DivX ? Movies/[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi : title: Avida year: 2006 language: french source: DVD other: Rip video_codec: Xvid release_group: PROD ? Movies/Alice in Wonderland DVDRip.XviD-DiAMOND/dmd-aw.avi : title: Alice in Wonderland source: DVD other: Rip video_codec: Xvid release_group: DiAMOND ? Movies/Ne.Le.Dis.A.Personne.Fr 2 cd/personnea_mp.avi : title: Ne Le Dis A Personne language: french cd_count: 2 ? Movies/Bunker Palace Hôtel (Enki Bilal) (1989)/Enki Bilal - Bunker Palace Hotel (Fr Vhs Rip).avi : title: Bunker Palace Hôtel year: 1989 language: french source: VHS other: Rip ? Movies/21 (2008)/21.(2008).DVDRip.x264.AC3-FtS.[sharethefiles.com].mkv : title: "21" year: 2008 source: DVD other: Rip video_codec: H.264 audio_codec: Dolby Digital release_group: FtS website: sharethefiles.com ? Movies/9 (2009)/9.2009.Blu-ray.DTS.720p.x264.HDBRiSe.[sharethefiles.com].mkv : title: "9" year: 2009 source: Blu-ray audio_codec: DTS screen_size: 720p video_codec: H.264 release_group: HDBRiSe website: sharethefiles.com ? Movies/Mamma.Mia.2008.DVDRip.AC3.XviD-CrazyTeam/Mamma.Mia.2008.DVDRip.AC3.XviD-CrazyTeam.avi : title: Mamma Mia year: 2008 source: DVD other: Rip audio_codec: Dolby Digital video_codec: Xvid release_group: CrazyTeam ? Movies/M.A.S.H. (1970)/MASH.(1970).[Divx.5.02][Dual-Subtitulos][DVDRip].ogm : title: MASH year: 1970 video_codec: DivX source: DVD other: [Dual Audio, Rip] ? Movies/The Doors (1991)/09.03.08.The.Doors.(1991).BDRip.720p.AC3.X264-HiS@SiLUHD-English.[sharethefiles.com].mkv : title: The Doors year: 1991 date: 2008-03-09 source: Blu-ray other: Rip screen_size: 720p audio_codec: Dolby Digital video_codec: H.264 release_group: HiS@SiLUHD language: english website: sharethefiles.com ? Movies/The Doors (1991)/08.03.09.The.Doors.(1991).BDRip.720p.AC3.X264-HiS@SiLUHD-English.[sharethefiles.com].mkv : options: --date-year-first title: The Doors year: 1991 date: 2008-03-09 source: Blu-ray other: Rip screen_size: 720p audio_codec: Dolby Digital video_codec: H.264 release_group: HiS@SiLUHD language: english website: sharethefiles.com ? Movies/Ratatouille/video_ts-ratatouille.srt : title: Ratatouille source: DVD # Removing this one because 001 is guessed as an episode number. # ? Movies/001 __ A classer/Fantomas se déchaine - Louis de Funès.avi # : title: Fantomas se déchaine ? Movies/Comme une Image (2004)/Comme.Une.Image.FRENCH.DVDRiP.XViD-NTK.par-www.divx-overnet.com.avi : title: Comme une Image year: 2004 language: french source: DVD other: Rip video_codec: Xvid release_group: NTK website: www.divx-overnet.com ? Movies/Fantastic Mr Fox/Fantastic.Mr.Fox.2009.DVDRip.{x264+LC-AAC.5.1}{Fr-Eng}{Sub.Fr-Eng}-™.[sharethefiles.com].mkv : title: Fantastic Mr Fox year: 2009 source: DVD other: Rip video_codec: H.264 audio_codec: AAC audio_profile: Low Complexity audio_channels: "5.1" language: [ french, english ] subtitle_language: [ french, english ] website: sharethefiles.com ? Movies/Somewhere.2010.DVDRip.XviD-iLG/i-smwhr.avi : title: Somewhere year: 2010 source: DVD other: Rip video_codec: Xvid release_group: iLG ? Movies/Moon_(2009).mkv : title: Moon year: 2009 ? Movies/Moon_(2009)-x02-Making_Of.mkv : title: Moon year: 2009 bonus: 2 bonus_title: Making Of ? movies/James_Bond-f17-Goldeneye.mkv : title: Goldeneye film_title: James Bond film: 17 ? /movies/James_Bond-f21-Casino_Royale.mkv : title: Casino Royale film_title: James Bond film: 21 ? /movies/James_Bond-f21-Casino_Royale-x01-Becoming_Bond.mkv : title: Casino Royale film_title: James Bond film: 21 bonus: 1 bonus_title: Becoming Bond ? /movies/James_Bond-f21-Casino_Royale-x02-Stunts.mkv : title: Casino Royale film_title: James Bond film: 21 bonus: 2 bonus_title: Stunts ? OSS_117--Cairo,_Nest_of_Spies.mkv : title: OSS 117 # TODO: Implement subTitle for movies. ? The Godfather Part 3.mkv ? The Godfather Part III.mkv : title: The Godfather part: 3 ? Foobar Part VI.mkv : title: Foobar part: 6 ? The_Insider-(1999)-x02-60_Minutes_Interview-1996.mp4 : title: The Insider year: 1999 bonus: 2 bonus_title: 60 Minutes Interview-1996 ? Rush.._Beyond_The_Lighted_Stage-x09-Between_Sun_and_Moon-2002_Hartford.mkv : title: Rush Beyond The Lighted Stage bonus: 9 bonus_title: Between Sun and Moon year: 2002 ? /public/uTorrent/Downloads Finished/Movies/Indiana.Jones.and.the.Temple.of.Doom.1984.HDTV.720p.x264.AC3.5.1-REDµX/Indiana.Jones.and.the.Temple.of.Doom.1984.HDTV.720p.x264.AC3.5.1-REDµX.mkv : title: Indiana Jones and the Temple of Doom year: 1984 source: HDTV screen_size: 720p video_codec: H.264 audio_codec: Dolby Digital audio_channels: "5.1" release_group: REDµX ? The.Director’s.Notebook.2006.Blu-Ray.x264.DXVA.720p.AC3-de[42].mkv : title: The Director’s Notebook year: 2006 source: Blu-ray video_codec: H.264 video_api: DXVA screen_size: 720p audio_codec: Dolby Digital release_group: de[42] ? Movies/Cosmopolis.2012.LiMiTED.720p.BluRay.x264-AN0NYM0US[bb]/ano-cosmo.720p.mkv : title: Cosmopolis year: 2012 screen_size: 720p video_codec: H.264 release_group: AN0NYM0US[bb] source: Blu-ray edition: Limited ? movies/La Science des Rêves (2006)/La.Science.Des.Reves.FRENCH.DVDRip.XviD-MP-AceBot.avi : title: La Science des Rêves year: 2006 source: DVD other: Rip video_codec: Xvid video_profile: Main release_group: AceBot language: French ? The_Italian_Job.mkv : title: The Italian Job ? The.Rum.Diary.2011.1080p.BluRay.DTS.x264.D-Z0N3.mkv : title: The Rum Diary year: 2011 screen_size: 1080p source: Blu-ray video_codec: H.264 audio_codec: DTS release_group: D-Z0N3 ? Life.Of.Pi.2012.1080p.BluRay.DTS.x264.D-Z0N3.mkv : title: Life Of Pi year: 2012 screen_size: 1080p source: Blu-ray video_codec: H.264 audio_codec: DTS release_group: D-Z0N3 ? The.Kings.Speech.2010.1080p.BluRay.DTS.x264.D Z0N3.mkv : title: The Kings Speech year: 2010 screen_size: 1080p source: Blu-ray audio_codec: DTS video_codec: H.264 release_group: D Z0N3 ? Street.Kings.2008.BluRay.1080p.DTS.x264.dxva EuReKA.mkv : title: Street Kings year: 2008 source: Blu-ray screen_size: 1080p audio_codec: DTS video_codec: H.264 video_api: DXVA release_group: EuReKA ? 2001.A.Space.Odyssey.1968.HDDVD.1080p.DTS.x264.dxva EuReKA.mkv : title: 2001 A Space Odyssey year: 1968 source: HD-DVD screen_size: 1080p audio_codec: DTS video_codec: H.264 video_api: DXVA release_group: EuReKA ? 2012.2009.720p.BluRay.x264.DTS WiKi.mkv : title: "2012" year: 2009 screen_size: 720p source: Blu-ray video_codec: H.264 audio_codec: DTS release_group: WiKi ? /share/Download/movie/Dead Man Down (2013) BRRiP XViD DD5_1 Custom NLSubs =-_lt Q_o_Q gt-=_/XD607ebb-BRc59935-5155473f-1c5f49/XD607ebb-BRc59935-5155473f-1c5f49.avi : title: Dead Man Down year: 2013 source: Blu-ray other: [Reencoded, Rip] video_codec: Xvid audio_channels: "5.1" audio_codec: Dolby Digital uuid: XD607ebb-BRc59935-5155473f-1c5f49 ? Pacific.Rim.3D.2013.COMPLETE.BLURAY-PCH.avi : title: Pacific Rim year: 2013 source: Blu-ray other: - Complete - 3D release_group: PCH ? Immersion.French.2011.STV.READNFO.QC.FRENCH.ENGLISH.NTSC.DVDR.nfo : title: Immersion French year: 2011 language: - French - English source: DVD other: [Straight to Video, Read NFO, NTSC] ? Immersion.French.2011.STV.READNFO.QC.FRENCH.NTSC.DVDR.nfo : title: Immersion French year: 2011 language: French source: DVD other: [Straight to Video, Read NFO, NTSC] ? Immersion.French.2011.STV.READNFO.QC.NTSC.DVDR.nfo : title: Immersion language: French year: 2011 source: DVD other: [Straight to Video, Read NFO, NTSC] ? French.Immersion.2011.STV.READNFO.QC.ENGLISH.NTSC.DVDR.nfo : title: French Immersion year: 2011 language: ENGLISH source: DVD other: [Straight to Video, Read NFO, NTSC] ? Howl's_Moving_Castle_(2004)_[720p,HDTV,x264,DTS]-FlexGet.avi : video_codec: H.264 source: HDTV title: Howl's Moving Castle screen_size: 720p year: 2004 audio_codec: DTS release_group: FlexGet ? Pirates de langkasuka.2008.FRENCH.1920X1080.h264.AVC.AsiaRa.mkv : screen_size: 1080p year: 2008 language: French video_codec: H.264 title: Pirates de langkasuka release_group: AsiaRa ? Masala (2013) Telugu Movie HD DVDScr XviD - Exclusive.avi : year: 2013 video_codec: Xvid title: Masala source: HD-DVD other: Screener release_group: Exclusive ? Django Unchained 2012 DVDSCR X264 AAC-P2P.nfo : year: 2012 other: Screener video_codec: H.264 title: Django Unchained audio_codec: AAC source: DVD release_group: P2P ? Ejecutiva.En.Apuros(2009).BLURAY.SCR.Xvid.Spanish.LanzamientosD.nfo : year: 2009 other: Screener source: Blu-ray video_codec: Xvid language: Spanish title: Ejecutiva En Apuros ? Die.Schluempfe.2.German.DL.1080p.BluRay.x264-EXQUiSiTE.mkv : title: Die Schluempfe 2 source: Blu-ray language: - Multiple languages - German video_codec: H.264 release_group: EXQUiSiTE screen_size: 1080p ? Rocky 1976 French SubForced BRRip x264 AC3-FUNKY.mkv : title: Rocky year: 1976 subtitle_language: French source: Blu-ray other: [Reencoded, Rip] video_codec: H.264 audio_codec: Dolby Digital release_group: FUNKY ? REDLINE (BD 1080p H264 10bit FLAC) [3xR].mkv : title: REDLINE source: Blu-ray video_codec: H.264 color_depth: 10-bit audio_codec: FLAC screen_size: 1080p ? The.Lizzie.McGuire.Movie.(2003).HR.DVDRiP.avi : title: The Lizzie McGuire Movie year: 2003 source: DVD other: [High Resolution, Rip] ? Hua.Mulan.BRRIP.MP4.x264.720p-HR.avi : title: Hua Mulan video_codec: H.264 source: Blu-ray screen_size: 720p other: [Reencoded, Rip] release_group: HR ? Dr.Seuss.The.Lorax.2012.DVDRip.LiNE.XviD.AC3.HQ.Hive-CM8.mp4 : video_codec: Xvid title: Dr Seuss The Lorax source: DVD other: [Rip, Line Audio] year: 2012 audio_codec: Dolby Digital audio_profile: High Quality release_group: Hive-CM8 ? "Star Wars: Episode IV - A New Hope (2004) Special Edition.MKV" : title: "Star Wars: Episode IV" alternative_title: A New Hope year: 2004 edition: Special ? Dr.LiNE.The.Lorax.2012.DVDRip.LiNE.XviD.AC3.HQ.Hive-CM8.mp4 : video_codec: Xvid title: Dr LiNE The Lorax source: DVD other: [Rip, Line Audio] year: 2012 audio_codec: Dolby Digital audio_profile: High Quality release_group: Hive-CM8 ? Dr.LiNE.The.Lorax.2012.DVDRip.XviD.AC3.HQ.Hive-CM8.mp4 : video_codec: Xvid title: Dr LiNE The Lorax source: DVD other: Rip year: 2012 audio_codec: Dolby Digital audio_profile: High Quality release_group: Hive-CM8 ? Perfect Child-2007-TRUEFRENCH-TVRip.Xvid-h@mster.avi : release_group: h@mster title: Perfect Child video_codec: Xvid language: French source: TV other: Rip year: 2007 ? entre.ciel.et.terre.(1994).dvdrip.h264.aac-psypeon.avi : audio_codec: AAC source: DVD other: Rip release_group: psypeon title: entre ciel et terre video_codec: H.264 year: 1994 ? Yves.Saint.Laurent.2013.FRENCH.DVDSCR.MD.XviD-ViVARiUM.avi : source: DVD language: French other: [Screener, Mic Dubbed] release_group: ViVARiUM title: Yves Saint Laurent video_codec: Xvid year: 2013 ? Echec et Mort - Hard to Kill - Steven Seagal Multi 1080p BluRay x264 CCATS.avi : source: Blu-ray language: Multiple languages release_group: CCATS screen_size: 1080p title: Echec et Mort alternative_title: - Hard to Kill - Steven Seagal video_codec: H.264 ? Paparazzi - Timsit/Lindon (MKV 1080p tvripHD) : options: -n title: Paparazzi alternative_title: - Timsit - Lindon screen_size: 1080p container: mkv source: HDTV other: Rip ? some.movie.720p.bluray.x264-mind : title: some movie screen_size: 720p video_codec: H.264 release_group: mind source: Blu-ray ? Dr LiNE The Lorax 720p h264 BluRay : title: Dr LiNE The Lorax screen_size: 720p video_codec: H.264 source: Blu-ray #TODO: Camelcase implementation #? BeatdownFrenchDVDRip.mkv #: options: -c # title: Beatdown # language: French # source: DVD #? YvesSaintLaurent2013FrenchDVDScrXvid.avi #: options: -c # source: DVD # language: French # other: Screener # title: Yves saint laurent # video_codec: Xvid # year: 2013 ? Elle.s.en.va.720p.mkv : screen_size: 720p title: Elle s en va ? FooBar.7.PDTV-FlexGet : source: Digital TV release_group: FlexGet title: FooBar 7 ? h265 - HEVC Riddick Unrated Director Cut French 1080p DTS.mkv : audio_codec: DTS edition: [Unrated, Director's Cut] language: fr screen_size: 1080p title: Riddick video_codec: H.265 ? "[h265 - HEVC] Riddick Unrated Director Cut French [1080p DTS].mkv" : audio_codec: DTS edition: [Unrated, Director's Cut] language: fr screen_size: 1080p title: Riddick video_codec: H.265 ? Barbecue-2014-French-mHD-1080p : language: fr other: Micro HD screen_size: 1080p title: Barbecue year: 2014 ? Underworld Quadrilogie VO+VFF+VFQ 1080p HDlight.x264~Tonyk~Monde Infernal : language: fr other: [Original Video, Micro HD] screen_size: 1080p title: Underworld Quadrilogie video_codec: H.264 ? A Bout Portant (The Killers).PAL.Multi.DVD-R-KZ : source: DVD language: mul release_group: KZ title: A Bout Portant ? "Mise à Sac (Alain Cavalier, 1967) [Vhs.Rip.Vff]" : source: VHS language: fr title: "Mise à Sac" year: 1967 ? A Bout Portant (The Killers).PAL.Multi.DVD-R-KZ : source: DVD other: PAL language: mul release_group: KZ title: A Bout Portant ? Youth.In.Revolt.(Be.Bad).2009.MULTI.1080p.LAME3*92-MEDIOZZ : audio_codec: MP3 language: mul release_group: MEDIOZZ screen_size: 1080p title: Youth In Revolt year: 2009 ? La Defense Lincoln (The Lincoln Lawyer) 2011 [DVDRIP][Vostfr] : source: DVD other: Rip subtitle_language: fr title: La Defense Lincoln year: 2011 ? '[h265 - HEVC] Fight Club French 1080p DTS.' : audio_codec: DTS language: fr screen_size: 1080p title: Fight Club video_codec: H.265 ? Love Gourou (Mike Myers) - FR : language: fr title: Love Gourou ? '[h265 - hevc] transformers 2 1080p french ac3 6ch.' : audio_channels: '5.1' audio_codec: Dolby Digital language: fr screen_size: 1080p title: transformers 2 video_codec: H.265 ? 1.Angry.Man.1957.mkv : title: 1 Angry Man year: 1957 ? 12.Angry.Men.1957.mkv : title: 12 Angry Men year: 1957 ? 123.Angry.Men.1957.mkv : title: 123 Angry Men year: 1957 ? "Looney Tunes 1444x866 Porky's Last Stand.mkv" : screen_size: 1444x866 title: Looney Tunes ? Das.Appartement.German.AC3D.DL.720p.BluRay.x264-TVP : audio_codec: Dolby Digital source: Blu-ray language: - German - Multi release_group: TVP screen_size: 720p title: Das Appartement type: movie video_codec: H.264 ? Das.Appartement.GERMAN.AC3D.DL.720p.BluRay.x264-TVP : audio_codec: Dolby Digital source: Blu-ray language: - de - mul release_group: TVP screen_size: 720p title: Das Appartement video_codec: H.264 ? Hyena.Road.2015.German.1080p.DL.DTSHD.Bluray.x264-pmHD : audio_codec: DTS-HD source: Blu-ray language: - de - mul release_group: pmHD screen_size: 1080p title: Hyena Road type: movie video_codec: H.264 year: 2015 ? Hyena.Road.2015.German.1080p.DL.DTSHD.Bluray.x264-pmHD : audio_codec: DTS-HD source: Blu-ray language: - de - mul release_group: pmHD screen_size: 1080p title: Hyena Road type: movie video_codec: H.264 year: 2015 ? Name.BDMux.720p : title: Name source: Blu-ray other: Mux screen_size: 720p type: movie ? Name.BRMux.720p : title: Name source: Blu-ray other: [Reencoded, Mux] screen_size: 720p type: movie ? Name.BDRipMux.720p : title: Name source: Blu-ray other: [Rip, Mux] screen_size: 720p type: movie ? Name.BRRipMux.720p : title: Name source: Blu-ray other: [Reencoded, Rip, Mux] screen_size: 720p type: movie ? Secondary Education (2013).mkv : options: -T Second title: Secondary Education year: 2013 type: movie ? Mad Max Beyond Thunderdome () : title: Mad Max Beyond Thunderdome type: movie ? Hacksaw Ridge 2016 Multi 2160p UHD BluRay Hevc10 HDR10 DTSHD & ATMOS 7.1 -DDR.mkv : title: Hacksaw Ridge year: 2016 language: mul screen_size: 2160p source: Ultra HD Blu-ray video_codec: H.265 color_depth: 10-bit audio_codec: [DTS-HD, Dolby Atmos] audio_channels: '7.1' release_group: DDR container: mkv type: movie ? Special.Correspondents.2016.iTA.ENG.4K.2160p.NetflixUHD.TeamPremium.mp4 : title: Special Correspondents year: 2016 language: [it, en] screen_size: 2160p streaming_service: Netflix other: Ultra HD release_group: TeamPremium container: mp4 type: movie ? -Special.Correspondents.2016.iTA.ENG.4K.2160p.NetflixUHD.TeamPremium.mp4 : alternative_title: 4K ? -Special.Correspondents.2016.iTA.ENG.4K.2160p.NetflixUHD.TeamPremium.mp4 : alternative_title: 2160p ? Suicide Squad EXTENDED (2016) 2160p 4K UltraHD Blu-Ray x265 (HEVC 10bit BT709) Dolby Atmos 7.1 -DDR : title: Suicide Squad edition: Extended year: 2016 screen_size: 2160p source: Ultra HD Blu-ray video_codec: H.265 color_depth: 10-bit audio_codec: Dolby Atmos audio_channels: '7.1' release_group: DDR type: movie ? Queen - A Kind of Magic (Alternative Extended Version) 2CD 2014 : title: Queen alternative_title: A Kind of Magic edition: [Alternative Cut, Extended] cd_count: 2 year: 2014 type: movie ? Jour.de.Fete.1949.ALTERNATiVE.CUT.1080p.BluRay.x264-SADPANDA[rarbg] : title: Jour de Fete year: 1949 edition: Alternative Cut screen_size: 1080p source: Blu-ray video_codec: H.264 release_group: SADPANDA[rarbg] ? The.Movie.CONVERT.720p.HDTV.x264-C4TV : title: The Movie other: Converted screen_size: 720p source: HDTV video_codec: H.264 release_group: C4TV type: movie ? Its.A.Wonderful.Life.1946.Colorized.720p.BRRip.999MB.MkvCage.com : title: Its A Wonderful Life year: 1946 other: [Colorized, Reencoded, Rip] screen_size: 720p source: Blu-ray size: 999MB website: MkvCage.com type: movie ? Alien DC (1979) [1080p] : title: Alien edition: Director's Cut year: 1979 screen_size: 1080p type: movie ? Requiem.For.A.Dream.2000.DC.1080p.BluRay.x264.anoXmous : title: Requiem For A Dream year: 2000 edition: Director's Cut screen_size: 1080p source: Blu-ray video_codec: H.264 release_group: anoXmous type: movie ? Before.the.Flood.2016.DOCU.1080p.WEBRip.x264.DD5.1-FGT : title: Before the Flood year: 2016 other: [Documentary, Rip] screen_size: 1080p source: Web video_codec: H.264 audio_codec: Dolby Digital audio_channels: '5.1' release_group: FGT type: movie ? Zootopia.2016.HDRip.1.46Gb.Dub.MegaPeer : title: Zootopia year: 2016 other: [HD, Rip] size: 1.46GB language: und release_group: MegaPeer type: movie ? Suntan.2016.FESTiVAL.DVDRip.x264-IcHoR : title: Suntan year: 2016 edition: Festival source: DVD other: Rip video_codec: H.264 release_group: IcHoR type: movie ? Hardwired.STV.NFOFiX.FRENCH.DVDRiP.XviD-SURViVAL : title: Hardwired other: [Straight to Video, Fix, Rip] language: french source: DVD video_codec: Xvid release_group: SURViVAL -proper_count: 1 type: movie ? Maze.Runner.The.Scorch.Trials.OM.2015.WEB-DLRip.by.Seven : title: Maze Runner The Scorch Trials other: [Open Matte, Rip] year: 2015 source: Web release_group: Seven type: movie ? Maze.Runner.The.Scorch.Trials.OM.2015.WEB-DLRip.by.Seven : title: Maze Runner The Scorch Trials other: [Open Matte, Rip] year: 2015 source: Web release_group: Seven type: movie ? Foo Bar 2015 Open Matte 1080p WEB-DL DD+5.1 H.264 : title: Foo Bar year: 2015 other: Open Matte screen_size: 1080p source: Web audio_codec: Dolby Digital Plus audio_channels: '5.1' video_codec: H.264 type: movie ? foo.bar.2015.open.matte.1080p.web-dl.dd+5.1.h.264 : title: foo bar year: 2015 other: Open Matte screen_size: 1080p source: Web audio_codec: Dolby Digital Plus audio_channels: '5.1' video_codec: H.264 type: movie ? Kampen Om Tungtvannet aka The Heavy Water War COMPLETE 720p x265 HEVC-Lund : title: Kampen Om Tungtvannet aka The Heavy Water War other: Complete screen_size: 720p video_codec: H.265 release_group: Lund type: movie ? All.Fall.Down.x264.PROOFFIX-OUTLAWS : title: All Fall Down video_codec: H.264 other: Fix release_group: OUTLAWS -proper_count: 1 type: movie ? The.Last.Survivors.2014.PROOF.SAMPLE.FiX.BDRip.x264-TOPCAT : title: The Last Survivors year: 2014 other: [Fix, Rip] source: Blu-ray video_codec: H.264 release_group: TOPCAT type: movie ? Bad Santa 2 2016 THEATRiCAL FRENCH BDRip XviD-EXTREME : title: Bad Santa 2 year: 2016 edition: Theatrical language: french source: Blu-ray other: Rip video_codec: Xvid release_group: EXTREME type: movie ? The Lord of the Rings The Fellowship of the Ring THEATRICAL EDITION (2001) [1080p] : title: The Lord of the Rings The Fellowship of the Ring edition: Theatrical year: 2001 screen_size: 1080p type: movie ? World War Z (2013) Theatrical Cut 720p BluRay x264 : title: World War Z year: 2013 edition: Theatrical screen_size: 720p source: Blu-ray video_codec: H.264 type: movie ? The Heartbreak Kid (1993) UNCUT 720p WEBRip x264 : title: The Heartbreak Kid year: 1993 edition: Uncut other: Rip screen_size: 720p source: Web video_codec: H.264 type: movie ? Mrs.Doubtfire.1993.720p.OAR.Bluray.DTS.x264-CtrlHD : title: Mrs Doubtfire year: 1993 screen_size: 720p other: Original Aspect Ratio source: Blu-ray audio_codec: DTS video_codec: H.264 release_group: CtrlHD type: movie ? Aliens.SE.1986.BDRip.1080p : title: Aliens edition: Special year: 1986 source: Blu-ray other: Rip screen_size: 1080p type: movie ? 10 Cloverfield Lane.[Blu-Ray 1080p].[MULTI] : options: --type movie title: 10 Cloverfield Lane source: Blu-ray screen_size: 1080p language: Multiple languages type: movie ? 007.Spectre.[HDTC.MD].[TRUEFRENCH] : options: --type movie title: 007 Spectre source: HD Telecine language: French type: movie ? We.Are.X.2016.LIMITED.BDRip.x264-BiPOLAR : title: We Are X year: 2016 edition: Limited source: Blu-ray other: Rip video_codec: H.264 release_group: BiPOLAR type: movie ? The Rack (VHS) [1956] Paul Newman : title: The Rack source: VHS year: 1956 type: movie ? Les.Magiciens.1976.VHSRip.XViD.MKO : title: Les Magiciens year: 1976 source: VHS other: Rip video_codec: Xvid release_group: MKO type: movie ? The Boss Baby 2017 720p CAM x264 AC3 TiTAN : title: The Boss Baby year: 2017 screen_size: 720p source: Camera video_codec: H.264 audio_codec: Dolby Digital release_group: TiTAN type: movie ? The.Boss.Baby.2017.HDCAM.XviD-MrGrey : title: The Boss Baby year: 2017 source: HD Camera video_codec: Xvid release_group: MrGrey type: movie ? The Martian 2015 Multi 2160p 4K UHD Bluray HEVC10 SDR DTSHD 7.1 -Zeus : title: The Martian year: 2015 language: mul screen_size: 2160p source: Ultra HD Blu-ray video_codec: H.265 color_depth: 10-bit other: Standard Dynamic Range audio_codec: DTS-HD audio_channels: '7.1' release_group: Zeus type: movie ? Fantastic Beasts and Where to Find Them 2016 Multi 2160p UHD BluRay HEVC HDR Atmos7.1-DDR : title: Fantastic Beasts and Where to Find Them year: 2016 language: mul screen_size: 2160p source: Ultra HD Blu-ray video_codec: H.265 other: HDR10 audio_codec: Dolby Atmos audio_channels: '7.1' release_group: DDR type: movie ? Life of Pi 2012 2160p 4K BluRay HDR10 HEVC BT2020 DTSHD 7.1 subs -DDR : title: Life of Pi year: 2012 screen_size: 2160p source: Ultra HD Blu-ray other: [HDR10, BT.2020] subtitle_language: und release_group: DDR ? Captain.America.Civil.War.HDR.1080p.HEVC.10bit.BT.2020.DTS-HD.MA.7.1-VISIONPLUSHDR : title: Captain America Civil War other: [HDR10, BT.2020] screen_size: 1080p video_codec: H.265 color_depth: 10-bit audio_codec: DTS-HD audio_profile: Master Audio audio_channels: '7.1' release_group: VISIONPLUSHDR type: movie ? Deadpool.2016.4K.2160p.UHD.HQ.8bit.BluRay.8CH.x265.HEVC-MZABI.mkv : title: Deadpool year: 2016 screen_size: 2160p source: Ultra HD Blu-ray other: High Quality color_depth: 8-bit audio_channels: '7.1' video_codec: H.265 release_group: MZABI type: movie ? Fantastic.Beasts.and.Where.to.Find.Them.2016.2160p.4K.UHD.10bit.HDR.BluRay.7.1.x265.HEVC-MZABI.mkv : title: Fantastic Beasts and Where to Find Them year: 2016 screen_size: 2160p source: Ultra HD Blu-ray color_depth: 10-bit other: HDR10 audio_channels: '7.1' video_codec: H.265 release_group: MZABI container: mkv type: movie ? The.Arrival.4K.HDR.HEVC.10bit.BT2020.DTS.HD-MA-MadVR.HDR10.Dolby.Vision-VISIONPLUSHDR1000 : title: The Arrival screen_size: 2160p other: [HDR10, BT.2020, Dolby Vision] video_codec: H.265 color_depth: 10-bit audio_codec: DTS-HD audio_profile: Master Audio release_group: VISIONPLUSHDR1000 type: movie ? Foo.Bar.2021.DV.2160p.WEB-DL.x265-ASDF : title: Foo Bar screen_size: 2160p other: [Dolby Vision] video_codec: H.265 release_group: ASDF type: movie ? How To Steal A Dog.2014.BluRay.1080p.12bit.HEVC.OPUS 5.1-Hn1Dr2.mkv : title: How To Steal A Dog year: 2014 source: Blu-ray screen_size: 1080p color_depth: 12-bit video_codec: H.265 audio_codec: Opus audio_channels: '5.1' release_group: Hn1Dr2 container: mkv type: movie ? Interstelar.2014.IMAX.RUS.BDRip.x264.-HELLYWOOD.mkv : title: Interstelar year: 2014 edition: IMAX language: ru source: Blu-ray other: Rip video_codec: H.264 release_group: HELLYWOOD container: mkv type: movie ? The.Dark.Knight.IMAX.EDITION.HQ.BluRay.1080p.x264.AC3.Hindi.Eng.ETRG : title: The Dark Knight edition: IMAX other: High Quality source: Blu-ray screen_size: 1080p video_codec: H.264 audio_codec: Dolby Digital language: [hindi, english] release_group: ETRG type: movie ? The.Martian.2015.4K.UHD.UPSCALED-ETRG : title: The Martian year: 2015 screen_size: 2160p other: [Ultra HD, Upscaled] release_group: ETRG type: movie ? Heathers.1988.1080p.BluRay.ARROW.4K.RESTORED.Plus.Comm.DTS.x264-MaG : title: Heathers edition: Restored year: 1988 screen_size: 1080p release_group: MaG type: movie ? The.Woman.2011.1080p.BluRay.4K.REMASTERED.Remux.AVC.DTS-HD.MA.5.1-PTP.mkv : title: The Woman edition: Remastered year: 2011 screen_size: 1080p release_group: PTP type: movie ? Delibal 2015 720p Upscale DVDRip x264 DD5.1 AC3 : title: Delibal year: 2015 screen_size: 720p other: [Upscaled, Rip] source: DVD video_codec: H.264 audio_codec: Dolby Digital audio_channels: '5.1' type: movie ? Casablanca [Ultimate Collector's Edition].1942.BRRip.XviD-VLiS : title: Casablanca edition: [Ultimate, Collector] year: 1942 source: Blu-ray other: [Reencoded, Rip] video_codec: Xvid release_group: VLiS type: movie ? Batman V Superman Dawn of Justice 2016 Extended Cut Ultimate Edition HDRip x264 AC3-DaDDy : title: Batman V Superman Dawn of Justice year: 2016 edition: [Extended, Ultimate] other: [HD, Rip] video_codec: H.264 audio_codec: Dolby Digital release_group: DaDDy type: movie ? Stargate SG1 Ultimate Fan Collection : title: Stargate SG1 edition: [Ultimate, Fan] ? The.Jungle.Book.2016.MULTi.1080p.BluRay.x264.DTS-HD.MA.7.1.DTS-HD.HRA.5.1-LeRalou : title: The Jungle Book year: 2016 language: mul screen_size: 1080p source: Blu-ray video_codec: H.264 audio_codec: DTS-HD audio_profile: [Master Audio, High Resolution Audio] audio_channels: ['7.1', '5.1'] release_group: LeRalou type: movie ? Terminus.2015.BluRay.1080p.x264.DTS-HD.HRA.5.1-LTT : title: Terminus year: 2015 source: Blu-ray screen_size: 1080p video_codec: H.264 audio_codec: DTS-HD audio_profile: High Resolution Audio audio_channels: '5.1' release_group: LTT type: movie ? Ghost.in.the.Shell.1995.1080p.Bluray.DTSES.x264-SHiTSoNy : title: Ghost in the Shell year: 1995 screen_size: 1080p source: Blu-ray audio_codec: DTS audio_profile: Extended Surround ? The.Boss.Baby.2017.BluRay.1080p.DTS-ES.x264-PRoDJi : title: The Boss Baby year: 2017 source: Blu-ray screen_size: 1080p audio_codec: DTS audio_profile: Extended Surround video_codec: H.264 release_group: PRoDJi type: movie ? Title.2000.720p.BluRay.DDEX.x264-HDClub.mkv : title: Title year: 2000 screen_size: 720p source: Blu-ray audio_codec: Dolby Digital audio_profile: EX video_codec: H.264 release_group: HDClub container: mkv type: movie ? Jack Reacher Never Go Back 2016 720p Bluray DD-EX x264-BluPanther : title: Jack Reacher Never Go Back year: 2016 screen_size: 720p source: Blu-ray audio_codec: Dolby Digital audio_profile: EX video_codec: H.264 release_group: BluPanther type: movie ? How To Steal A Dog.2014.BluRay.1080p.12bit.HEVC.OPUS 5.1-Hn1Dr2.mkv : title: How To Steal A Dog year: 2014 source: Blu-ray screen_size: 1080p color_depth: 12-bit video_codec: H.265 audio_codec: Opus audio_channels: '5.1' release_group: Hn1Dr2 container: mkv type: movie ? How.To.Be.Single.2016.1080p.BluRay.x264-BLOW/blow-how.to.be.single.2016.1080p.bluray.x264.mkv : title: How To Be Single year: 2016 screen_size: 1080p source: Blu-ray video_codec: H.264 release_group: BLOW container: mkv type: movie ? After.the.Storm.2016.720p.YIFY : title: After the Storm year: 2016 screen_size: 720p release_group: YIFY type: movie ? Battle Royale 2000 DC (1080p Bluray x265 HEVC 10bit AAC 7.1 Japanese Tigole) : title: Battle Royale year: 2000 edition: Director's Cut screen_size: 1080p source: Blu-ray video_codec: H.265 color_depth: 10-bit audio_codec: AAC audio_channels: '7.1' language: jp release_group: Tigole ? Congo.The.Grand.Inga.Project.2013.1080p.BluRay.x264-OBiTS : title: Congo The Grand Inga Project year: 2013 screen_size: 1080p source: Blu-ray video_codec: H.264 release_group: OBiTS type: movie ? Congo.The.Grand.Inga.Project.2013.BRRip.XviD.MP3-RARBG : title: Congo The Grand Inga Project year: 2013 source: Blu-ray other: [Reencoded, Rip] video_codec: Xvid audio_codec: MP3 release_group: RARBG type: movie ? Congo.The.Grand.Inga.Project.2013.720p.BluRay.H264.AAC-RARBG : title: Congo The Grand Inga Project year: 2013 screen_size: 720p source: Blu-ray video_codec: H.264 audio_codec: AAC release_group: RARBG type: movie ? Mit.dem.Bauch.durch.die.Wand.SWiSSGERMAN.DOKU.DVDRiP.x264-DEFLOW : title: Mit dem Bauch durch die Wand language: de-CH other: [Documentary, Rip] source: DVD video_codec: H.264 release_group: DEFLOW type: movie ? InDefinitely.Maybe.2008.1080p.EUR.BluRay.VC-1.DTS-HD.MA.5.1-FGT : title: InDefinitely Maybe year: 2008 screen_size: 1080p source: Blu-ray video_codec: VC-1 audio_codec: DTS-HD audio_profile: Master Audio audio_channels: '5.1' release_group: FGT type: movie ? Bjyukujyo Kyoushi Kan XXX 720P WEBRIP MP4-GUSH : title: Bjyukujyo Kyoushi Kan other: [XXX, Rip] screen_size: 720p source: Web container: mp4 release_group: GUSH type: movie ? The.Man.With.The.Golden.Arm.1955.1080p.BluRay.x264.DTS-FGT : title: The Man With The Golden Arm year: 1955 screen_size: 1080p source: Blu-ray video_codec: H.264 audio_codec: DTS release_group: FGT type: movie ? blow-how.to.be.single.2016.1080p.bluray.x264.mkv : release_group: blow title: how to be single year: 2016 screen_size: 1080p source: Blu-ray video_codec: H.264 container: mkv type: movie ? ulshd-the.right.stuff.1983.multi.1080p.bluray.x264.mkv : release_group: ulshd title: the right stuff year: 1983 language: mul screen_size: 1080p source: Blu-ray video_codec: H.264 container: mkv type: movie ? FROZEN [2010] LiMiTED DVDRip H262 AAC[ ENG SUBS]-MANTESH : title: FROZEN year: 2010 edition: Limited source: DVD other: Rip video_codec: MPEG-2 audio_codec: AAC subtitle_language: english release_group: MANTESH type: movie ? Family.Katta.2016.1080p.WEB-DL.H263.DD5.1.ESub-DDR : title: Family Katta year: 2016 screen_size: 1080p source: Web video_codec: H.263 audio_codec: Dolby Digital audio_channels: '5.1' subtitle_language: und release_group: DDR type: movie ? Bad Boys 2 1080i.mpg2.rus.eng.ts : title: Bad Boys 2 screen_size: 1080i video_codec: MPEG-2 language: [russian, english] container: ts type: movie ? Alien.Director.Cut.Ita.Eng.VP9.Opus.AlphaBot.webm : title: Alien edition: Director's Cut language: [english, italian] video_codec: VP9 audio_codec: Opus release_group: AlphaBot container: webm type: movie ? The.Stranger.1946.US.(Kino.Classics).Bluray.1080p.LPCM.DD-2.0.x264-Grym@BTNET : title: The Stranger year: 1946 country: US source: Blu-ray screen_size: 1080p audio_codec: [LPCM, Dolby Digital] audio_channels: '2.0' video_codec: H.264 release_group: Grym@BTNET type: movie ? X-Men.Apocalypse.2016.complete.hdts.pcm.TrueFrench-Scarface45.avi : title: X-Men Apocalypse year: 2016 other: Complete source: HD Telesync audio_codec: PCM language: french release_group: Scarface45 container: avi type: movie ? Tears.of.Steel.2012.2160p.DMRip.Eng.HDCLUB.mkv : title: Tears of Steel year: 2012 screen_size: 2160p source: Digital Master other: Rip language: english release_group: HDCLUB container: mkv type: movie ? "/Movies/Open Season 2 (2008)/Open Season 2 (2008) - Bluray-1080p.x264.DTS.mkv" : options: --type movie title: Open Season 2 year: 2008 source: Blu-ray screen_size: 1080p video_codec: H.264 audio_codec: DTS container: mkv type: movie ? Re-Animator.1985.INTEGRAL VERSION LIMITED EDITION.1080p.BluRay.REMUX.AVC.DTS-HD MA 5.1-LAZY : title: Re-Animator year: 1985 edition: Limited screen_size: 1080p source: Blu-ray other: Remux video_codec: H.264 audio_codec: DTS-HD audio_profile: Master Audio audio_channels: '5.1' release_group: LAZY type: movie ? The.Movie.2016.2160p.UHD.BluRay.REMUX.HDR.HEVC.DTS-X-NOGROUP : title: The Movie year: 2016 screen_size: 2160p source: Ultra HD Blu-ray other: - HDR10 - Remux video_codec: H.265 video_profile: High Efficiency Video Coding audio_codec: DTS:X release_group: NOGROUP type: movie ? Test (2013) [WEBDL-1080p] [x264 AC3] [ENG+RU+PT] [NTb].mkv : title: Test year: 2013 source: Web screen_size: 1080p video_codec: H.264 audio_codec: Dolby Digital language: [en, ru, pt] release_group: NTb container: mkv type: movie ? "[nextorrent.org] Bienvenue.Au.Gondwana.2016.FRENCH.DVDRiP.XViD-AViTECH.avi" : website: nextorrent.org title: Bienvenue Au Gondwana year: 2016 language: french source: DVD other: Rip video_codec: Xvid release_group: AViTECH container: avi type: movie ? Star Trek First Contact (1996) Blu-Ray 1080p24 H.264 TrueHD 5.1 CtrlHD : title: Star Trek First Contact year: 1996 source: Blu-ray screen_size: 1080p frame_rate: 24fps video_codec: H.264 audio_codec: Dolby TrueHD audio_channels: '5.1' release_group: CtrlHD type: movie ? The.Hobbit.The.Desolation.of.Smaug.Extended.HFR.48fps.ITA.ENG.AC3.BDRip.1080p.x264_ZMachine.mkv : title: The Hobbit The Desolation of Smaug edition: Extended other: [High Frame Rate, Rip] frame_rate: 48fps language: [it, en] audio_codec: Dolby Digital source: Blu-ray screen_size: 1080p video_codec: H.264 release_group: ZMachine container: mkv type: movie ? Test (2013) [WEBDL-1080p] [x264 AC3] [ENG+PT+DE] [STANDARD] : title: Test year: 2013 source: Web screen_size: 1080p video_codec: H.264 audio_codec: Dolby Digital language: [en, pt, de] release_group: STANDARD type: movie ? Test (2013) [WEBDL-1080p] [x264 AC3] [ENG+DE+IT] [STANDARD] : title: Test year: 2013 source: Web screen_size: 1080p video_codec: H.264 audio_codec: Dolby Digital language: [en, de, it] release_group: STANDARD type: movie ? Ant-Man.and.the.Wasp.2018.Digital.Extras.1080p.AMZN.WEB-DL.DDP5.1.H.264-NTG.mkv : title: Ant-Man and the Wasp year: 2018 other: Extras screen_size: 1080p streaming_service: Amazon Prime source: Web audio_codec: Dolby Digital Plus audio_channels: '5.1' video_codec: H.264 release_group: NTG type: movie ? Ant-Man.and.the.Wasp.2018.1080p.AMZN.WEB-DL.DDP5.1.H.264-NTG.mkv : title: Ant-Man and the Wasp year: 2018 screen_size: 1080p streaming_service: Amazon Prime source: Web audio_codec: Dolby Digital Plus audio_channels: '5.1' video_codec: H.264 release_group: NTG type: movie ? Avengers.Infinity.War.2018.3D.Hybrid.REPACK.1080p.BluRay.REMUX.AVC.Atmos-EPSiLON.mk3d : title: Avengers Infinity War year: 2018 other: - 3D - Hybrid - Proper - Remux proper_count: 1 screen_size: 1080p source: Blu-ray video_codec: H.264 audio_codec: Dolby Atmos release_group: EPSiLON container: mk3d type: movie ? Ouija.Seance.The.Final.Game.2018.1080p.WEB-DL.DD5.1.H264-CMRG : title: Ouija Seance The Final Game year: 2018 screen_size: 1080p source: Web audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: CMRG type: movie ? The.Girl.in.the.Spiders.Web.2019.1080p.WEB-DL.x264.AC3-EVO.mkv : title: The Girl in the Spiders Web year: 2019 screen_size: 1080p source: Web video_codec: H.264 audio_codec: Dolby Digital release_group: EVO container: mkv type: movie ? Kes.1969.1080p.BluRay.FLAC1.0.x264-DON.mkv : title: Kes year: 1969 screen_size: 1080p source: Blu-ray audio_codec: FLAC audio_channels: '1.0' video_codec: H.264 release_group: DON container: mkv type: movie guessit-3.8.0/guessit/test/rules/000077500000000000000000000000001453655371700170215ustar00rootroot00000000000000guessit-3.8.0/guessit/test/rules/__init__.py000066400000000000000000000001651453655371700211340ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=pointless-statement, missing-docstring, invalid-name guessit-3.8.0/guessit/test/rules/audio_codec.yml000066400000000000000000000033671453655371700220130ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use $ marker to check inputs that should not match results. ? +MP3 ? +lame ? +lame3.12 ? +lame3.100 : audio_codec: MP3 ? +MP2 : audio_codec: MP2 ? +DolbyDigital ? +DD ? +Dolby Digital ? +AC3 ? +AC-3 : audio_codec: Dolby Digital ? +DDP ? +DD+ ? +EAC3 ? +EAC-3 ? +E-AC-3 ? +E-AC3 : audio_codec: Dolby Digital Plus ? +DolbyAtmos ? +Dolby Atmos ? +Atmos ? -Atmosphere : audio_codec: Dolby Atmos ? +AAC : audio_codec: AAC ? +Flac : audio_codec: FLAC ? +DTS : audio_codec: DTS ? +True-HD ? +trueHD : audio_codec: Dolby TrueHD ? +True-HD51 ? +trueHD51 : audio_codec: Dolby TrueHD audio_channels: '5.1' ? +DTSHD ? +DTS HD ? +DTS-HD : audio_codec: DTS-HD ? +DTS-HDma ? +DTSMA : audio_codec: DTS-HD audio_profile: Master Audio ? +AC3-hq : audio_codec: Dolby Digital audio_profile: High Quality ? +AAC-HE : audio_codec: AAC audio_profile: High Efficiency ? +AAC-LC : audio_codec: AAC audio_profile: Low Complexity ? +AAC2.0 ? +AAC20 : audio_codec: AAC audio_channels: '2.0' ? +7.1 ? +7ch ? +8ch : audio_channels: '7.1' ? +5.1 ? +5ch ? +6ch : audio_channels: '5.1' ? +2ch ? +2.0 ? +stereo : audio_channels: '2.0' ? +1.0 ? +1ch ? +mono : audio_channels: '1.0' ? DD5.1 ? DD51 : audio_codec: Dolby Digital audio_channels: '5.1' ? -51 : audio_channels: '5.1' ? DTS-HD.HRA ? DTSHD.HRA ? DTS-HD.HR ? DTSHD.HR ? -HRA ? -HR : audio_codec: DTS-HD audio_profile: High Resolution Audio ? DTSES ? DTS-ES ? -ES : audio_codec: DTS audio_profile: Extended Surround ? DTS:X ? DTS-X ? DTSX : audio_codec: DTS:X ? DD-EX ? DDEX ? -EX : audio_codec: Dolby Digital audio_profile: EX ? OPUS : audio_codec: Opus ? Vorbis : audio_codec: Vorbis ? PCM : audio_codec: PCM ? LPCM : audio_codec: LPCM guessit-3.8.0/guessit/test/rules/bonus.yml000066400000000000000000000004611453655371700206730ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use - marker to check inputs that should not match results. ? Movie Title-x01-Other Title.mkv ? Movie Title-x01-Other Title ? directory/Movie Title-x01-Other Title/file.mkv : title: Movie Title bonus_title: Other Title bonus: 1 guessit-3.8.0/guessit/test/rules/cd.yml000066400000000000000000000003711453655371700201330ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use - marker to check inputs that should not match results. ? cd 1of3 : cd: 1 cd_count: 3 ? Some.Title-DVDRIP-x264-CDP : cd: !!null release_group: CDP video_codec: H.264 guessit-3.8.0/guessit/test/rules/common_words.yml000066400000000000000000000057621453655371700222640ustar00rootroot00000000000000? is : title: is ? it : title: it ? am : title: am ? mad : title: mad ? men : title: men ? man : title: man ? run : title: run ? sin : title: sin ? st : title: st ? to : title: to ? 'no' : title: 'no' ? non : title: non ? war : title: war ? min : title: min ? new : title: new ? car : title: car ? day : title: day ? bad : title: bad ? bat : title: bat ? fan : title: fan ? fry : title: fry ? cop : title: cop ? zen : title: zen ? gay : title: gay ? fat : title: fat ? one : title: one ? cherokee : title: cherokee ? got : title: got ? an : title: an ? as : title: as ? cat : title: cat ? her : title: her ? be : title: be ? hat : title: hat ? sun : title: sun ? may : title: may ? my : title: my ? mr : title: mr ? rum : title: rum ? pi : title: pi ? bb : title: bb ? bt : title: bt ? tv : title: tv ? aw : title: aw ? by : title: by ? md : other: Mic Dubbed ? mp : title: mp ? cd : title: cd ? in : title: in ? ad : title: ad ? ice : title: ice ? ay : title: ay ? at : title: at ? star : title: star ? so : title: so ? he : title: he ? do : title: do ? ax : title: ax ? mx : title: mx ? bas : title: bas ? de : title: de ? le : title: le ? son : title: son ? ne : title: ne ? ca : title: ca ? ce : title: ce ? et : title: et ? que : title: que ? mal : title: mal ? est : title: est ? vol : title: vol ? or : title: or ? mon : title: mon ? se : title: se ? je : title: je ? tu : title: tu ? me : title: me ? ma : title: ma ? va : title: va ? au : country: AU ? lu : title: lu ? wa : title: wa ? ga : title: ga ? ao : title: ao ? la : title: la ? el : title: el ? del : title: del ? por : title: por ? mar : title: mar ? al : title: al ? un : title: un ? ind : title: ind ? arw : title: arw ? ts : source: Telesync ? ii : title: ii ? bin : title: bin ? chan : title: chan ? ss : title: ss ? san : title: san ? oss : title: oss ? iii : title: iii ? vi : title: vi ? ben : title: ben ? da : title: da ? lt : title: lt ? ch : title: ch ? sr : title: sr ? ps : title: ps ? cx : title: cx ? vo : title: vo ? mkv : container: mkv ? avi : container: avi ? dmd : title: dmd ? the : title: the ? dis : title: dis ? cut : title: cut ? stv : title: stv ? des : title: des ? dia : title: dia ? and : title: and ? cab : title: cab ? sub : title: sub ? mia : title: mia ? rim : title: rim ? las : title: las ? une : title: une ? par : title: par ? srt : container: srt ? ano : title: ano ? toy : title: toy ? job : title: job ? gag : title: gag ? reel : title: reel ? www : title: www ? for : title: for ? ayu : title: ayu ? csi : title: csi ? ren : title: ren ? moi : title: moi ? sur : title: sur ? fer : title: fer ? fun : title: fun ? two : title: two ? big : title: big ? psy : title: psy ? air : title: air ? brazil : title: brazil ? jordan : title: jordan ? bs : title: bs ? kz : title: kz ? gt : title: gt ? im : title: im ? pt : language: pt ? scr : title: scr ? sd : title: sd ? hr : other: High Resolution guessit-3.8.0/guessit/test/rules/country.yml000066400000000000000000000004621453655371700212510ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use $ marker to check inputs that should not match results. ? Us.this.is.title ? this.is.title.US : country: US title: this is title ? This.is.Us : title: This is Us ? This.Is.Us : options: --no-default-config title: This Is Us guessit-3.8.0/guessit/test/rules/date.yml000066400000000000000000000014521453655371700204630ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use - marker to check inputs that should not match results. ? +09.03.08 ? +09.03.2008 ? +2008.03.09 : date: 2008-03-09 ? +31.01.15 ? +31.01.2015 ? +15.01.31 ? +2015.01.31 : date: 2015-01-31 ? +01.02.03 : date: 2003-02-01 ? +01.02.03 : options: --date-year-first date: 2001-02-03 ? +01.02.03 : options: --date-day-first date: 2003-02-01 ? 1919 ? 2030 : !!map {} ? 2029 : year: 2029 ? (1920) : year: 1920 ? 2012 : year: 2012 ? 2011 2013 (2012) (2015) # first marked year is guessed. : title: "2011 2013" year: 2012 ? 2012 2009 S01E02 2015 # If no year is marked, the second one is guessed. : title: "2012" year: 2009 episode_title: "2015" ? Something 2 mar 2013) : title: Something date: 2013-03-02 type: episode guessit-3.8.0/guessit/test/rules/edition.yml000066400000000000000000000025571453655371700212100ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use - marker to check inputs that should not match results. ? Director's cut ? Edition Director's cut : edition: Director's Cut ? Collector ? Collector Edition ? Edition Collector : edition: Collector ? Special Edition ? Edition Special ? -Special : edition: Special ? Criterion Edition ? Criterion Collection ? Edition Criterion ? CC : edition: Criterion ? Deluxe ? Deluxe Edition ? Edition Deluxe : edition: Deluxe ? Super Movie Alternate XViD ? Super Movie Alternative XViD ? Super Movie Alternate Cut XViD ? Super Movie Alternative Cut XViD : edition: Alternative Cut ? Remaster ? Remastered ? 4k-Remaster ? 4k-Remastered ? 4k Remaster ? 4k Remastered : edition: Remastered ? Restore ? Restored ? 4k-Restore ? 4k-Restored ? 4k Restore ? 4k Restored : edition: Restored ? ddc : edition: Director's Definitive Cut ? IMAX ? IMAX Edition : edition: IMAX ? ultimate edition ? -ultimate : edition: Ultimate ? ultimate collector edition ? ultimate collector's edition ? ultimate collectors edition ? -collectors edition ? -ultimate edition : edition: [Ultimate, Collector] ? ultimate collectors edition dc : edition: [Ultimate, Collector, Director's Cut] ? fan edit ? fan edition ? fan collection : edition: Fan ? ultimate fan edit ? ultimate fan edition ? ultimate fan collection : edition: [Ultimate, Fan] guessit-3.8.0/guessit/test/rules/episodes.yml000066400000000000000000000134451453655371700213660ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use $ marker to check inputs that should not match results. ? +2x5 ? +2X5 ? +02x05 ? +2X05 ? +02x5 ? S02E05 ? s02e05 ? s02e5 ? s2e05 ? s02ep05 ? s2EP5 ? -s03e05 ? -s02e06 ? -3x05 ? -2x06 : season: 2 episode: 5 ? "+0102" ? "+102" : season: 1 episode: 2 ? "0102 S03E04" ? "S03E04 102" : season: 3 episode: 4 ? +serie Saison 2 other ? +serie Season 2 other ? +serie Saisons 2 other ? +serie Seasons 2 other ? +serie Season Two other ? +serie Season II other : season: 2 ? Some Series.S02E01.Episode.title.mkv ? Some Series/Season 02/E01-Episode title.mkv ? Some Series/Season 02/Some Series-E01-Episode title.mkv ? Some Dummy Directory/Season 02/Some Series-E01-Episode title.mkv ? -Some Dummy Directory/Season 02/E01-Episode title.mkv ? Some Series/Unsafe Season 02/Some Series-E01-Episode title.mkv ? -Some Series/Unsafe Season 02/E01-Episode title.mkv ? Some Series/Season 02/E01-Episode title.mkv ? Some Series/ Season 02/E01-Episode title.mkv ? Some Dummy Directory/Some Series S02/E01-Episode title.mkv ? Some Dummy Directory/S02 Some Series/E01-Episode title.mkv : title: Some Series episode_title: Episode title season: 2 episode: 1 ? Some Series.S02E01.mkv ? Some Series/Season 02/E01.mkv ? Some Series/Season 02/Some Series-E01.mkv ? Some Dummy Directory/Season 02/Some Series-E01.mkv ? -Some Dummy Directory/Season 02/E01.mkv ? Some Series/Unsafe Season 02/Some Series-E01.mkv ? -Some Series/Unsafe Season 02/E01.mkv ? Some Series/Season 02/E01.mkv ? Some Series/ Season 02/E01.mkv ? Some Dummy Directory/Some Series S02/E01-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.mkv : title: Some Series season: 2 episode: 1 ? Some Series S03E01E02 : title: Some Series season: 3 episode: [1, 2] ? Some Series S01S02S03 ? Some Series S01-02-03 ? Some Series S01 S02 S03 ? Some Series S01 02 03 : title: Some Series season: [1, 2, 3] ? Some Series E01E02E03 ? Some Series E01-02-03 ? Some Series E01-03 ? Some Series E01 E02 E03 ? Some Series E01 02 03 : title: Some Series episode: [1, 2, 3] ? Some Series E01E02E04 ? Some Series E01 E02 E04 ? Some Series E01 02 04 : title: Some Series episode: [1, 2, 4] ? Some Series E01-02-04 ? Some Series E01-04 ? Some Series E01-04 : title: Some Series episode: [1, 2, 3, 4] ? Some Series E01-02-E04 : title: Some Series episode: [1, 2, 3, 4] ? Episode 3 ? -Episode III : episode: 3 ? Episode 3 ? Episode III : options: -t episode episode: 3 ? -A very special movie : episode_details: Special ? -A very special episode : options: -t episode episode_details: Special ? A very special episode s06 special : options: -t episode title: A very special episode episode_details: Special ? 12 Monkeys\Season 01\Episode 05\12 Monkeys - S01E05 - The Night Room.mkv : container: mkv title: 12 Monkeys episode: 5 season: 1 ? S03E02.X.1080p : episode: 2 screen_size: 1080p season: 3 ? Something 1 x 2-FlexGet : options: -t episode title: Something season: 1 episode: 2 episode_title: FlexGet ? Show.Name.-.Season.1.to.3.-.Mp4.1080p ? Show.Name.-.Season.1~3.-.Mp4.1080p ? Show.Name.-.Saison.1.a.3.-.Mp4.1080p : container: mp4 screen_size: 1080p season: - 1 - 2 - 3 title: Show Name ? Show.Name.Season.1.3&5.HDTV.XviD-GoodGroup[SomeTrash] ? Show.Name.Season.1.3 and 5.HDTV.XviD-GoodGroup[SomeTrash] : source: HDTV release_group: GoodGroup[SomeTrash] season: - 1 - 3 - 5 title: Show Name type: episode video_codec: Xvid ? Show.Name.Season.1.2.3-5.HDTV.XviD-GoodGroup[SomeTrash] ? Show.Name.Season.1.2.3~5.HDTV.XviD-GoodGroup[SomeTrash] ? Show.Name.Season.1.2.3 to 5.HDTV.XviD-GoodGroup[SomeTrash] : source: HDTV release_group: GoodGroup[SomeTrash] season: - 1 - 2 - 3 - 4 - 5 title: Show Name type: episode video_codec: Xvid ? The.Get.Down.S01EP01.FRENCH.720p.WEBRIP.XVID-STR : episode: 1 source: Web other: Rip language: fr release_group: STR screen_size: 720p season: 1 title: The Get Down type: episode video_codec: Xvid ? My.Name.Is.Earl.S01E01-S01E21.SWE-SUB : episode: - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 - 21 season: 1 subtitle_language: sv title: My Name Is Earl type: episode ? Show.Name.Season.4.Episodes.1-12 : episode: - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 season: 4 title: Show Name type: episode ? show name s01.to.s04 : season: - 1 - 2 - 3 - 4 title: show name type: episode ? epi : options: -t episode title: epi ? Episode20 ? Episode 20 : episode: 20 ? Episode50 ? Episode 50 : episode: 50 ? Episode51 ? Episode 51 : episode: 51 ? Episode70 ? Episode 70 : episode: 70 ? Episode71 ? Episode 71 : episode: 71 ? S01D02.3-5-GROUP : disc: [2, 3, 4, 5] ? S01D02&4-6&8 : disc: [2, 4, 5, 6, 8] ? Something.4x05-06 ? Something - 4x05-06 ? Something:4x05-06 ? Something 4x05-06 ? Something-4x05-06 : title: Something season: 4 episode: - 5 - 6 ? Something.4x05-06 ? Something - 4x05-06 ? Something:4x05-06 ? Something 4x05-06 ? Something-4x05-06 : options: -T something title: Something season: 4 episode: - 5 - 6 ? Colony 23/S01E01.Some.title.mkv : title: Colony 23 season: 1 episode: 1 episode_title: Some title ? Show.Name.E02.2010.mkv : options: -t episode title: Show Name year: 2010 episode: 2 ? Show.Name.E02.S2010.mkv : options: -t episode title: Show Name year: 2010 season: 2010 episode: 2 ? Show.Name.E02.2010.mkv : title: Show Name year: 2010 episode: 2 ? Show.Name.E02.S2010.mkv : title: Show Name year: 2010 season: 2010 episode: 2 ? Show Name - S32-Dummy 45-Ep 6478 : title: Show Name episode_title: Dummy 45 season: 32 episode: 6478 ? Show Name - S32-Week 45-Ep 6478 : title: Show Name season: 32 week: 45 episode: 6478 guessit-3.8.0/guessit/test/rules/film.yml000066400000000000000000000004571453655371700205010ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use - marker to check inputs that should not match results. ? Film Title-f01-Series Title.mkv ? Film Title-f01-Series Title ? directory/Film Title-f01-Series Title/file.mkv : title: Series Title film_title: Film Title film: 1 guessit-3.8.0/guessit/test/rules/language.yml000066400000000000000000000015601453655371700213310ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use - marker to check inputs that should not match results. ? +English ? .ENG. : language: English ? +French : language: French ? +SubFrench ? +SubFr ? +STFr ? ST.FR : subtitle_language: French ? +ENG.-.sub.FR ? ENG.-.FR Sub ? +ENG.-.SubFR ? +ENG.-.FRSUB ? +ENG.-.FRSUBS ? +ENG.-.FR-SUBS : language: English subtitle_language: French ? "{Fr-Eng}.St{Fr-Eng}" ? "Le.Prestige[x264.{Fr-Eng}.St{Fr-Eng}.Chaps].mkv" : language: [French, English] subtitle_language: [French, English] ? +ENG.-.sub.SWE ? ENG.-.SWE Sub ? +ENG.-.SubSWE ? +ENG.-.SWESUB ? +ENG.-.sub.SV ? ENG.-.SV Sub ? +ENG.-.SubSV ? +ENG.-.SVSUB : language: English subtitle_language: Swedish ? The English Patient (1996) : title: The English Patient -language: english ? French.Kiss.1995.1080p : title: French Kiss -language: french guessit-3.8.0/guessit/test/rules/other.yml000066400000000000000000000041271453655371700206710ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use - marker to check inputs that should not match results. ? +DVDSCR ? +DVDScreener ? +DVD-SCR ? +DVD Screener ? +DVD AnythingElse Screener ? -DVD AnythingElse SCR : other: Screener ? +AudioFix ? +AudioFixed ? +Audio Fix ? +Audio Fixed : other: Audio Fixed ? +SyncFix ? +SyncFixed ? +Sync Fix ? +Sync Fixed : other: Sync Fixed ? +DualAudio ? +Dual Audio : other: Dual Audio ? +ws ? +WideScreen ? +Wide Screen : other: Widescreen # Fix must be surround by others properties to be matched. ? DVD.fix.XViD ? -DVD.Fix ? -Fix.XViD : other: Fix -proper_count: 1 ? -DVD.BlablaBla.Fix.Blablabla.XVID ? -DVD.BlablaBla.Fix.XVID ? -DVD.Fix.Blablabla.XVID : other: Fix -proper_count: 1 ? DVD.Real.PROPER.REPACK : other: Proper proper_count: 3 ? Proper.720p ? +Repack ? +Rerip : other: Proper proper_count: 1 ? XViD.Fansub : other: Fan Subtitled ? XViD.Fastsub : other: Fast Subtitled ? +Season Complete ? -Complete : other: Complete ? R5 : other: Region 5 ? RC : other: Region C ? PreAir ? Pre Air : other: Preair ? Screener : other: Screener ? Remux : other: Remux ? Hybrid : other: Hybrid ? 3D.2019 : other: 3D ? HD : other: HD ? FHD ? FullHD ? Full HD : other: Full HD ? UHD ? Ultra ? UltraHD ? Ultra HD : other: Ultra HD ? mHD # ?? ? HDLight : other: Micro HD ? HQ : other: High Quality ? hr : other: High Resolution ? PAL : other: PAL ? SECAM : other: SECAM ? NTSC : other: NTSC ? LDTV : other: Low Definition ? LD : other: Line Dubbed ? MD : other: Mic Dubbed ? -The complete movie : other: Complete ? +The complete movie : title: The complete movie ? +AC3-HQ : audio_profile: High Quality ? Other-HQ : other: High Quality ? reenc ? re-enc ? re-encoded ? reencoded : other: Reencoded ? CONVERT XViD : other: Converted ? +HDRIP # it's a Rip from non specified HD source : other: [HD, Rip] ? SDR : other: Standard Dynamic Range ? HDR ? HDR10 ? -HDR100 : other: HDR10 ? BT2020 ? BT.2020 ? -BT.20200 ? -BT.2021 : other: BT.2020 ? Upscaled ? Upscale : other: Upscaled ? REPACK5 ? ReRip5 : other: Proper proper_count: 5guessit-3.8.0/guessit/test/rules/part.yml000066400000000000000000000005401453655371700205110ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use - marker to check inputs that should not match results. ? Filename Part 3.mkv ? Filename Part III.mkv ? Filename Part Three.mkv ? Filename Part Trois.mkv : title: Filename part: 3 ? Part 3 ? Part III ? Part Three ? Part Trois ? Part3 : part: 3 ? -Something.Apt.1 : part: 1guessit-3.8.0/guessit/test/rules/processors.yml000066400000000000000000000004241453655371700217460ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use $ marker to check inputs that should not match results. # Prefer information for last path. ? Some movie (2000)/Some movie (2001).mkv ? Some movie (2001)/Some movie.mkv : year: 2001 container: mkv guessit-3.8.0/guessit/test/rules/processors_test.py000066400000000000000000000022321453655371700226330ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=pointless-statement, missing-docstring, invalid-name, pointless-string-statement from rebulk.match import Matches, Match from ...rules.processors import StripSeparators def test_strip_separators(): strip_separators = StripSeparators() matches = Matches() m = Match(3, 11, input_string="pre.ABCDEF.post") assert m.raw == '.ABCDEF.' matches.append(m) returned_matches = strip_separators.when(matches, None) assert returned_matches == matches strip_separators.then(matches, returned_matches, None) assert m.raw == 'ABCDEF' def test_strip_separators_keep_acronyms(): strip_separators = StripSeparators() matches = Matches() m = Match(0, 13, input_string=".S.H.I.E.L.D.") m2 = Match(0, 22, input_string=".Agent.Of.S.H.I.E.L.D.") assert m.raw == '.S.H.I.E.L.D.' matches.append(m) matches.append(m2) returned_matches = strip_separators.when(matches, None) assert returned_matches == matches strip_separators.then(matches, returned_matches, None) assert m.raw == '.S.H.I.E.L.D.' assert m2.raw == 'Agent.Of.S.H.I.E.L.D.' guessit-3.8.0/guessit/test/rules/release_group.yml000066400000000000000000000034741453655371700224100ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use - marker to check inputs that should not match results. ? Some.Title.XViD-ReleaseGroup ? Some.Title.XViD-ReleaseGroup.mkv : release_group: ReleaseGroup ? Some.Title.XViD-by.Artik[SEDG].avi : release_group: Artik[SEDG] ? "[ABC] Some.Title.avi" ? some/folder/[ABC]Some.Title.avi : release_group: ABC ? "[ABC] Some.Title.XViD-GRP.avi" ? some/folder/[ABC]Some.Title.XViD-GRP.avi : release_group: GRP ? "[ABC] Some.Title.S01E02.avi" ? some/folder/[ABC]Some.Title.S01E02.avi : release_group: ABC ? Some.Title.XViD-S2E02.NoReleaseGroup.avi : release_group: !!null ? Test.S01E01-FooBar-Group : options: -G group -G xxxx episode: 1 episode_title: FooBar release_group: Group season: 1 title: Test type: episode ? Test.S01E01-FooBar-Group : options: -G re:gr.?up -G xxxx episode: 1 episode_title: FooBar release_group: Group season: 1 title: Test type: episode ? Show.Name.x264-byEMP : title: Show Name video_codec: H.264 release_group: byEMP ? Show.Name.x264-NovaRip : title: Show Name video_codec: H.264 release_group: NovaRip ? Show.Name.x264-PARTiCLE : title: Show Name video_codec: H.264 release_group: PARTiCLE ? Show.Name.x264-POURMOi : title: Show Name video_codec: H.264 release_group: POURMOi ? Show.Name.x264-RipPourBox : title: Show Name video_codec: H.264 release_group: RipPourBox ? Show.Name.x264-RiPRG : title: Show Name video_codec: H.264 release_group: RiPRG ? Archer (2009) S13E01 The Big Con (1080p AMZN Webrip x265 10bit EAC3 5.1 - JBENT)[TAoE] : release_group: JBENT TAoE ? Dark Phoenix (2019) (1080p BluRay x265 HEVC 10bit AAC 7.1 Tigole) [QxR] : release_group: Tigole QxR ? The Peripheral (2022) Season 1 S01 (1080p AMZN WEB-DL x265 HEVC 10bit DDP5.1 D0ct0rLew) [SEV] : release_group: D0ct0rLew SEVguessit-3.8.0/guessit/test/rules/screen_size.yml000066400000000000000000000072361453655371700220650ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use - marker to check inputs that should not match results. ? +360p ? +360px ? -360 ? +500x360 ? -250x360 : screen_size: 360p ? +640x360 ? -640x360i ? -684x360i : screen_size: 360p aspect_ratio: 1.778 ? +360i : screen_size: 360i ? +480x360i ? -480x360p ? -450x360 : screen_size: 360i aspect_ratio: 1.333 ? +368p ? +368px ? -368i ? -368 ? +500x368 : screen_size: 368p ? -490x368 ? -700x368 : screen_size: 368p ? +492x368p : screen_size: aspect_ratio: 1.337 ? +654x368 : screen_size: 368p aspect_ratio: 1.777 ? +698x368 : screen_size: 368p aspect_ratio: 1.897 ? +368i : -screen_size: 368i ? +480p ? +480px ? -480i ? -480 ? -500x480 ? -638x480 ? -920x480 : screen_size: 480p ? +640x480 : screen_size: 480p aspect_ratio: 1.333 ? +852x480 : screen_size: 480p aspect_ratio: 1.775 ? +910x480 : screen_size: 480p aspect_ratio: 1.896 ? +500x480 ? +500 x 480 ? +500 * 480 ? +500x480p ? +500X480i : screen_size: 500x480 aspect_ratio: 1.042 ? +480i ? +852x480i : screen_size: 480i ? +540p ? +540px ? -540i ? -540 : screen_size: 540p ? +540i : screen_size: 540i ? +576p ? +576px ? -576i ? -576 ? -500x576 ? -766x576 ? -1094x576 : screen_size: 576p ? +768x576 : screen_size: 576p aspect_ratio: 1.333 ? +1024x576 : screen_size: 576p aspect_ratio: 1.778 ? +1092x576 : screen_size: 576p aspect_ratio: 1.896 ? +500x576 : screen_size: 500x576 aspect_ratio: 0.868 ? +576i : screen_size: 576i ? +720p ? +720px ? -720i ? 720hd ? 720pHD ? -720 ? -500x720 ? -950x720 ? -1368x720 : screen_size: 720p ? +960x720 : screen_size: 720p aspect_ratio: 1.333 ? +1280x720 : screen_size: 720p aspect_ratio: 1.778 ? +1366x720 : screen_size: 720p aspect_ratio: 1.897 ? +500x720 : screen_size: 500x720 aspect_ratio: 0.694 ? +900p ? +900px ? -900i ? -900 ? -500x900 ? -1198x900 ? -1710x900 : screen_size: 900p ? +1200x900 : screen_size: 900p aspect_ratio: 1.333 ? +1600x900 : screen_size: 900p aspect_ratio: 1.778 ? +1708x900 : screen_size: 900p aspect_ratio: 1.898 ? +500x900 ? +500x900p ? +500x900i : screen_size: 500x900 aspect_ratio: 0.556 ? +900i : screen_size: 900i ? +1080p ? +1080px ? +1080hd ? +1080pHD ? -1080i ? -1080 ? -500x1080 ? -1438x1080 ? -2050x1080 : screen_size: 1080p ? +1440x1080 : screen_size: 1080p aspect_ratio: 1.333 ? +1920x1080 : screen_size: 1080p aspect_ratio: 1.778 ? +2048x1080 : screen_size: 1080p aspect_ratio: 1.896 ? +1080i ? -1080p : screen_size: 1080i ? 1440p : screen_size: 1440p ? +500x1080 : screen_size: 500x1080 aspect_ratio: 0.463 ? +2160p ? +2160px ? -2160i ? -2160 ? +4096x2160 ? +4k ? -2878x2160 ? -4100x2160 : screen_size: 2160p ? +2880x2160 : screen_size: 2160p aspect_ratio: 1.333 ? +3840x2160 : screen_size: 2160p aspect_ratio: 1.778 ? +4098x2160 : screen_size: 2160p aspect_ratio: 1.897 ? +500x2160 : screen_size: 500x2160 aspect_ratio: 0.231 ? +4320p ? +4320px ? -4320i ? -4320 ? -5758x2160 ? -8198x2160 : screen_size: 4320p ? +5760x4320 : screen_size: 4320p aspect_ratio: 1.333 ? +7680x4320 : screen_size: 4320p aspect_ratio: 1.778 ? +8196x4320 : screen_size: 4320p aspect_ratio: 1.897 ? +500x4320 : screen_size: 500x4320 aspect_ratio: 0.116 ? Test.File.720hd.bluray ? Test.File.720p24 ? Test.File.720p30 ? Test.File.720p50 ? Test.File.720p60 ? Test.File.720p120 : screen_size: 720p ? Test.File.400p : options: advanced_config: screen_size: progressive: ["400"] screen_size: 400p ? Test.File2.400p : options: advanced_config: screen_size: progressive: ["400"] screen_size: 400p ? Test.File.720p : options: advanced_config: screen_size: progressive: ["400"] screen_size: 720p guessit-3.8.0/guessit/test/rules/size.yml000066400000000000000000000001041453655371700205110ustar00rootroot00000000000000? 1.1tb : size: 1.1TB ? 123mb : size: 123MB ? 4.3gb : size: 4.3GB guessit-3.8.0/guessit/test/rules/source.yml000066400000000000000000000074541453655371700210560ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use - marker to check inputs that should not match results. ? +VHS ? -VHSAnythingElse ? -SomeVHS stuff ? -VH ? -VHx : source: VHS -other: Rip ? +VHSRip ? +VHS-Rip ? +VhS_rip ? +VHS.RIP ? -VHS ? -VHxRip : source: VHS other: Rip ? +Cam : source: Camera -other: Rip ? +CamRip ? +CaM Rip ? +Cam_Rip ? +cam.rip ? -Cam : source: Camera other: Rip ? +HDCam ? +HD-Cam : source: HD Camera -other: Rip ? +HDCamRip ? +HD-Cam.rip ? -HDCam ? -HD-Cam : source: HD Camera other: Rip ? +Telesync ? +TS : source: Telesync -other: Rip ? +TelesyncRip ? +TSRip ? -Telesync ? -TS : source: Telesync other: Rip ? +HD TS ? -Hd.Ts # ts file extension ? -HD.TS # ts file extension ? +Hd-Ts : source: HD Telesync -other: Rip ? +HD TS Rip ? +Hd-Ts-Rip ? -HD TS ? -Hd-Ts : source: HD Telesync other: Rip ? +Workprint ? +workPrint ? +WorkPrint ? +WP ? -Work Print : source: Workprint -other: Rip ? +Telecine ? +teleCine ? +TC ? -Tele Cine : source: Telecine -other: Rip ? +Telecine Rip ? +teleCine-Rip ? +TC-Rip ? -Telecine ? -TC : source: Telecine other: Rip ? +HD-TELECINE ? +HDTC : source: HD Telecine -other: Rip ? +HD-TCRip ? +HD TELECINE RIP ? -HD-TELECINE ? -HDTC : source: HD Telecine other: Rip ? +PPV : source: Pay-per-view -other: Rip ? +ppv-rip ? -PPV : source: Pay-per-view other: Rip ? -TV ? +SDTV ? +TV-Dub : source: TV -other: Rip ? +SDTVRIP ? +Rip sd tv ? +TvRip ? +Rip TV ? -TV ? -SDTV : source: TV other: Rip ? +DVB ? +pdTV ? +Pd Tv : source: Digital TV -other: Rip ? +DVB-Rip ? +DvBRiP ? +pdtvRiP ? +pd tv RiP ? -DVB ? -pdTV ? -Pd Tv : source: Digital TV other: Rip ? +DVD ? +video ts ? +DVDR ? +DVD 9 ? +dvd 5 ? -dvd ts : source: DVD -source: Telesync -other: Rip ? +DVD-RIP ? -video ts ? -DVD ? -DVDR ? -DVD 9 ? -dvd 5 : source: DVD other: Rip ? +HDTV : source: HDTV -other: Rip ? +tv rip hd ? +HDtv Rip ? -HdRip # it's a Rip from non specified HD source ? -HDTV : source: HDTV other: Rip ? +VOD : source: Video on Demand -other: Rip ? +VodRip ? +vod rip ? -VOD : source: Video on Demand other: Rip ? +webrip ? +Web Rip ? +webdlrip ? +web dl rip ? +webcap ? +web cap ? +webcaprip ? +web cap rip : source: Web other: Rip ? +webdl ? +Web DL ? +webHD ? +WEB hd ? +web : source: Web -other: Rip ? +HDDVD ? +hd dvd : source: HD-DVD -other: Rip ? +hdDvdRip ? -HDDVD ? -hd dvd : source: HD-DVD other: Rip ? +BluRay ? +BD ? +BD5 ? +BD9 ? +BD25 ? +bd50 : source: Blu-ray -other: Rip ? +BR-Scr ? +BR.Screener : source: Blu-ray other: [Reencoded, Screener] -language: pt-BR ? +BR-Rip ? +BRRip : source: Blu-ray other: [Reencoded, Rip] -language: pt-BR ? +BluRay rip ? +BDRip ? -BluRay ? -BD ? -BR ? -BR rip ? -BD5 ? -BD9 ? -BD25 ? -bd50 : source: Blu-ray other: Rip ? XVID.NTSC.DVDR.nfo : source: DVD -other: Rip ? +AHDTV : source: Analog HDTV -other: Rip ? +dsr ? +dth : source: Satellite -other: Rip ? +dsrip ? +ds rip ? +dsrrip ? +dsr rip ? +satrip ? +sat rip ? +dthrip ? +dth rip ? -dsr ? -dth : source: Satellite other: Rip ? +UHDTV : source: Ultra HDTV -other: Rip ? +UHDRip ? +UHDTV Rip ? -UHDTV : source: Ultra HDTV other: Rip ? UHD Bluray ? UHD 2160p Bluray ? UHD 8bit Bluray ? UHD HQ 8bit Bluray ? Ultra Bluray ? Ultra HD Bluray ? Bluray ULTRA ? Bluray Ultra HD ? Bluray UHD ? 4K Bluray ? 2160p Bluray ? UHD 10bit HDR Bluray ? UHD HDR10 Bluray ? -HD Bluray ? -AMERICAN ULTRA (2015) 1080p Bluray ? -American.Ultra.2015.BRRip ? -BRRip XviD AC3-ULTRAS ? -UHD Proper Bluray : source: Ultra HD Blu-ray ? UHD.BRRip ? UHD.2160p.BRRip ? BRRip.2160p.UHD ? BRRip.[4K-2160p-UHD] : source: Ultra HD Blu-ray other: [Reencoded, Rip] ? UHD.2160p.BDRip ? BDRip.[4K-2160p-UHD] : source: Ultra HD Blu-ray other: Rip ? DM : source: Digital Master ? DMRIP ? DM-RIP : source: Digital Master other: Rip guessit-3.8.0/guessit/test/rules/title.yml000066400000000000000000000021511453655371700206640ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use - marker to check inputs that should not match results. ? Title Only ? -Title XViD 720p Only ? sub/folder/Title Only ? -sub/folder/Title XViD 720p Only ? Title Only.mkv ? Title Only.avi : title: Title Only ? Title Only/title_only.mkv : title: Title Only ? title_only.mkv : title: title only ? Some Title/some.title.mkv ? some.title/Some.Title.mkv : title: Some Title ? SOME TITLE/Some.title.mkv ? Some.title/SOME TITLE.mkv : title: Some title ? some title/Some.title.mkv ? Some.title/some title.mkv : title: Some title ? Some other title/Some.Other.title.mkv ? Some.Other title/Some other title.mkv : title: Some Other title ? This T.I.T.L.E. has dots ? This.T.I.T.L.E..has.dots : title: This T.I.T.L.E has dots ? This.T.I.T.L.E..has.dots.S01E02.This E.P.T.I.T.L.E.has.dots : title: This T.I.T.L.E has dots season: 1 episode: 2 episode_title: This E.P.T.I.T.L.E has dots type: episode ? /mydatapool/mydata/Videos/Shows/C/Caprica/Season 1/Apotheosis_1920x1080.mp4 : title: Caprica episode_title: Apotheosis season: 1 type: episode guessit-3.8.0/guessit/test/rules/video_codec.yml000066400000000000000000000022331453655371700220070ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use - marker to check inputs that should not match results. ? rv10 ? rv13 ? RV20 ? Rv30 ? rv40 ? -xrv40 : video_codec: RealVideo ? mpeg2 ? MPEG2 ? MPEG-2 ? mpg2 ? H262 ? H.262 ? x262 ? -mpeg ? -xmpeg2 ? -mpeg2x : video_codec: MPEG-2 ? DivX ? -div X ? divx ? dvdivx ? DVDivX : video_codec: DivX ? XviD ? xvid ? -x vid : video_codec: Xvid ? h263 ? x263 ? h.263 : video_codec: H.263 ? h264 ? x264 ? h.264 ? x.264 ? AVC ? AVCHD ? -MPEG-4 ? -mpeg4 ? -mpeg ? -h 265 ? -x265 : video_codec: H.264 ? h265 ? x265 ? h.265 ? x.265 ? hevc ? -h 264 ? -x264 : video_codec: H.265 ? hevc10 ? HEVC-YUV420P10 : video_codec: H.265 color_depth: 10-bit ? h265-HP : video_codec: H.265 video_profile: High ? H.264-SC : video_codec: H.264 video_profile: Scalable Video Coding ? mpeg4-AVC : video_codec: H.264 video_profile: Advanced Video Codec High Definition ? AVCHD-SC ? H.264-AVCHD-SC : video_codec: H.264 video_profile: - Scalable Video Coding - Advanced Video Codec High Definition ? VC1 ? VC-1 : video_codec: VC-1 ? VP7 : video_codec: VP7 ? VP8 ? VP80 : video_codec: VP8 ? VP9 : video_codec: VP9 guessit-3.8.0/guessit/test/rules/website.yml000066400000000000000000000012011453655371700212000ustar00rootroot00000000000000# Multiple input strings having same expected results can be chained. # Use - marker to check inputs that should not match results. ? +tvu.org.ru ? -tvu.unsafe.ru : website: tvu.org.ru ? +www.nimp.na ? -somewww.nimp.na ? -www.nimp.nawouak ? -nimp.na : website: www.nimp.na ? +wawa.co.uk ? -wawa.uk : website: wawa.co.uk ? -Dark.Net.S01E06.720p.HDTV.x264-BATV -Dark.Net.2015.720p.HDTV.x264-BATV : website: Dark.Net ? Dark.Net.S01E06.720p.HDTV.x264-BATV Dark.Net.2015.720p.HDTV.x264-BATV : title: Dark Net ? www.4MovieRulz.be - Ginny Weds Sunny (2020) 1080p Hindi Proper HDRip x264 DD5.1 - 2.4GB ESub.mkv : website: www.4MovieRulz.be guessit-3.8.0/guessit/test/streaming_services.yaml000066400000000000000000001470551453655371700224630ustar00rootroot00000000000000? House.of.Cards.2013.S02E03.1080p.NF.WEBRip.DD5.1.x264-NTb.mkv ? House.of.Cards.2013.S02E03.1080p.Netflix.WEBRip.DD5.1.x264-NTb.mkv : title: House of Cards year: 2013 season: 2 episode: 3 screen_size: 1080p streaming_service: Netflix source: Web other: Rip audio_channels: "5.1" audio_codec: Dolby Digital video_codec: H.264 release_group: NTb ? The.Daily.Show.2015.07.01.Kirsten.Gillibrand.Extended.720p.CC.WEBRip.AAC2.0.x264-BTW.mkv ? The.Daily.Show.2015.07.01.Kirsten.Gillibrand.Extended.720p.ComedyCentral.WEBRip.AAC2.0.x264-BTW.mkv ? The.Daily.Show.2015.07.01.Kirsten.Gillibrand.Extended.720p.Comedy.Central.WEBRip.AAC2.0.x264-BTW.mkv : audio_channels: '2.0' audio_codec: AAC date: 2015-07-01 edition: Extended source: Web other: Rip release_group: BTW screen_size: 720p streaming_service: Comedy Central title: The Daily Show episode_title: Kirsten Gillibrand video_codec: H.264 ? The.Daily.Show.2015.07.01.Kirsten.Gillibrand.Extended.Interview.720p.CC.WEBRip.AAC2.0.x264-BTW.mkv : audio_channels: '2.0' audio_codec: AAC date: 2015-07-01 source: Web release_group: BTW screen_size: 720p streaming_service: Comedy Central title: The Daily Show episode_title: Kirsten Gillibrand Extended Interview video_codec: H.264 ? The.Daily.Show.2015.07.02.Sarah.Vowell.CC.WEBRip.AAC2.0.x264-BTW.mkv : audio_channels: '2.0' audio_codec: AAC date: 2015-07-02 source: Web release_group: BTW streaming_service: Comedy Central title: The Daily Show episode_title: Sarah Vowell video_codec: H.264 # Streaming service: Amazon ? Show.Name.S07E04.Service.1080p.AMZN.WEBRip.DD+5.1.x264 ? Show.Name.S07E04.Service.1080p.AMZN-CBR.WEBRip.DD+5.1.x264 ? Show.Name.S07E04.Service.1080p.AmazonPrime.WEBRip.DD+5.1.x264 : title: Show Name season: 7 episode: 4 episode_title: Service screen_size: 1080p streaming_service: Amazon Prime source: Web other: Rip audio_codec: Dolby Digital Plus audio_channels: '5.1' video_codec: H.264 type: episode # Streaming service: Comedy Central ? Show.Name.2016.09.28.Nice.Title.Extended.1080p.CC.WEBRip.AAC2.0.x264-monkee : title: Show Name date: 2016-09-28 episode_title: Nice Title edition: Extended other: Rip screen_size: 1080p streaming_service: Comedy Central source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: The CW ? Show.Name.US.S12E20.Nice.Title.720p.CW.WEBRip.AAC2.0.x264-monkee ? Show.Name.US.S12E20.Nice.Title.720p.TheCW.WEBRip.AAC2.0.x264-monkee : title: Show Name country: US season: 12 episode: 20 episode_title: Nice Title screen_size: 720p streaming_service: The CW source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: AMBC ? Show.Name.2016.09.27.Nice.Title.720p.AMBC.WEBRip.AAC2.0.x264-monkee : title: Show Name date: 2016-09-27 episode_title: Nice Title screen_size: 720p streaming_service: ABC source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: HIST ? Show.Name.720p.HIST.WEBRip.AAC2.0.H.264-monkee ? Show.Name.720p.History.WEBRip.AAC2.0.H.264-monkee : options: -t episode title: Show Name screen_size: 720p streaming_service: History source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: PBS ? Show.Name.2015.Nice.Title.1080p.PBS.WEBRip.AAC2.0.H264-monkee : options: -t episode title: Show Name year: 2015 episode_title: Nice Title screen_size: 1080p streaming_service: PBS source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: SeeSo ? Show.Name.2016.Nice.Title.1080p.SESO.WEBRip.AAC2.0.x264-monkee : options: -t episode title: Show Name year: 2016 episode_title: Nice Title screen_size: 1080p streaming_service: SeeSo source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: Discovery ? Show.Name.S01E03.Nice.Title.720p.DISC.WEBRip.AAC2.0.x264-NTb ? Show.Name.S01E03.Nice.Title.720p.Discovery.WEBRip.AAC2.0.x264-NTb : title: Show Name season: 1 episode: 3 episode_title: Nice Title screen_size: 720p streaming_service: Discovery source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: NTb type: episode # Streaming service: BBC iPlayer ? Show.Name.2016.08.18.Nice.Title.720p.iP.WEBRip.AAC2.0.H.264-monkee ? Show.Name.2016.08.18.Nice.Title.720p.BBCiPlayer.WEBRip.AAC2.0.H.264-monkee : title: Show Name date: 2016-08-18 episode_title: Nice Title streaming_service: BBC iPlayer screen_size: 720p source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: A&E ? Show.Name.S15E18.Nice.Title.720p.AE.WEBRip.AAC2.0.H.264-monkee ? Show.Name.S15E18.Nice.Title.720p.A&E.WEBRip.AAC2.0.H.264-monkee : title: Show Name season: 15 episode: 18 episode_title: Nice Title screen_size: 720p streaming_service: A&E source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: Adult Swim ? Show.Name.S04E01.Nice.Title.1080p.AS.WEBRip.AAC2.0.H.264-monkee ? Show.Name.S04E01.Nice.Title.1080p.AdultSwim.WEBRip.AAC2.0.H.264-monkee : title: Show Name season: 4 episode: 1 episode_title: Nice Title screen_size: 1080p streaming_service: Adult Swim source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: Netflix ? Show.Name.2013.S02E03.1080p.NF.WEBRip.DD5.1.x264-NTb.mkv : title: Show Name year: 2013 season: 2 episode: 3 screen_size: 1080p streaming_service: Netflix source: Web other: Rip audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: NTb container: mkv type: episode # Streaming service: CBS ? Show.Name.2016.05.10.Nice.Title.720p.CBS.WEBRip.AAC2.0.x264-monkee : title: Show Name date: 2016-05-10 episode_title: Nice Title screen_size: 720p streaming_service: CBS source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: NBA TV ? NBA.2016.02.27.Team.A.vs.Team.B.720p.NBA.WEBRip.AAC2.0.H.264-monkee ? NBA.2016.02.27.Team.A.vs.Team.B.720p.NBATV.WEBRip.AAC2.0.H.264-monkee : title: NBA date: 2016-02-27 episode_title: Team A vs Team B screen_size: 720p streaming_service: NBA TV source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: ePix ? Show.Name.S05E04.Nice.Title.Part4.720p.EPIX.WEBRip.AAC2.0.H.264-monkee ? Show.Name.S05E04.Nice.Title.Part4.720p.ePix.WEBRip.AAC2.0.H.264-monkee : title: Show Name season: 5 episode: 4 episode_title: Nice Title part: 4 screen_size: 720p streaming_service: ePix source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: NBC ? Show.Name.S41E03.Nice.Title.720p.NBC.WEBRip.AAC2.0.x264-monkee : title: Show Name season: 41 episode: 3 episode_title: Nice Title screen_size: 720p streaming_service: NBC source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: Syfy ? Show.Name.S01E02.Nice.Title.720p.SYFY.WEBRip.AAC2.0.x264-group ? Show.Name.S01E02.Nice.Title.720p.Syfy.WEBRip.AAC2.0.x264-group : title: Show Name season: 1 episode: 2 episode_title: Nice Title screen_size: 720p streaming_service: Syfy source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: group type: episode # Streaming service: Spike TV ? Show.Name.S01E02.Nice.Title.720p.SPKE.WEBRip.AAC2.0.x264-group ? Show.Name.S01E02.Nice.Title.720p.Spike TV.WEBRip.AAC2.0.x264-group ? Show.Name.S01E02.Nice.Title.720p.SpikeTV.WEBRip.AAC2.0.x264-group : title: Show Name season: 1 episode: 2 episode_title: Nice Title screen_size: 720p streaming_service: Spike TV source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: group type: episode # Streaming service: IFC ? Show.Name.S01E02.Nice.Title.720p.IFC.WEBRip.AAC2.0.x264-group : title: Show Name season: 1 episode: 2 episode_title: Nice Title screen_size: 720p streaming_service: IFC source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: group type: episode # Streaming service: NATG ? Show.Name.S01E02.Nice.Title.720p.NATG.WEBRip.AAC2.0.x264-group ? Show.Name.S01E02.Nice.Title.720p.NationalGeographic.WEBRip.AAC2.0.x264-group : title: Show Name season: 1 episode: 2 episode_title: Nice Title screen_size: 720p streaming_service: National Geographic source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: group type: episode # Streaming service: NFL ? Show.Name.S01E02.Nice.Title.720p.NFL.WEBRip.AAC2.0.x264-group : title: Show Name season: 1 episode: 2 episode_title: Nice Title screen_size: 720p streaming_service: NFL source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: group type: episode # Streaming service: UFC ? Show.Name.S01E02.Nice.Title.720p.UFC.WEBRip.AAC2.0.x264-group : title: Show Name season: 1 episode: 2 episode_title: Nice Title screen_size: 720p streaming_service: UFC source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: group type: episode # Streaming service: TV Land ? Show.Name.S01E02.Nice.Title.720p.TVL.WEBRip.AAC2.0.x264-group ? Show.Name.S01E02.Nice.Title.720p.TVLand.WEBRip.AAC2.0.x264-group ? Show.Name.S01E02.Nice.Title.720p.TV Land.WEBRip.AAC2.0.x264-group : title: Show Name season: 1 episode: 2 episode_title: Nice Title screen_size: 720p streaming_service: TV Land source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: group type: episode # Streaming service: Crunchy Roll ? Show.Name.S01.1080p.CR.WEBRip.AAC.2.0.x264-monkee : title: Show Name season: 1 screen_size: 1080p streaming_service: Crunchy Roll source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: Disney ? Show.Name.S01.1080p.DSNY.WEBRip.AAC.2.0.x264-monkee ? Show.Name.S01.1080p.Disney.WEBRip.AAC.2.0.x264-monkee : title: Show Name season: 1 screen_size: 1080p streaming_service: Disney source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: Nickelodeon ? Show.Name.S01.1080p.NICK.WEBRip.AAC.2.0.x264-monkee ? Show.Name.S01.1080p.Nickelodeon.WEBRip.AAC.2.0.x264-monkee ? Show.Name.S01.1080p.NICKAPP.WEBRip.AAC.2.0.x264-monkee : title: Show Name season: 1 screen_size: 1080p streaming_service: Nickelodeon source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: TFou ? Show.Name.S01.1080p.TFOU.WEBRip.AAC.2.0.x264-monkee ? Show.Name.S01.1080p.TFou.WEBRip.AAC.2.0.x264-monkee : title: Show Name season: 1 screen_size: 1080p streaming_service: TFou source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: monkee type: episode # Streaming service: DIY Network ? Show.Name.S01.720p.DIY.WEBRip.AAC2.0.H.264-BTN : title: Show Name season: 1 screen_size: 720p streaming_service: DIY Network source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: BTN type: episode # Streaming service: USA Network ? Show.Name.S01E02.Exfil.1080p.USAN.WEBRip.AAC2.0.x264-AJP69 : title: Show Name season: 1 episode: 2 screen_size: 1080p streaming_service: USA Network source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: AJP69 type: episode # Streaming service: TV3 Ireland ? Show.Name.S01E08.576p.TV3.WEBRip.AAC2.0.x264-HARiKEN : title: Show Name season: 1 episode: 8 screen_size: 576p streaming_service: TV3 Ireland source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: HARiKEN type: episode # Streaming service: TV4 Sweeden ? Show.Name.S05.720p.TV4.WEBRip.AAC2.0.H.264-BTW : title: Show Name season: 5 screen_size: 720p streaming_service: TV4 Sweeden source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: BTW type: episode # Streaming service: TLC ? Show.Name.S02.720p.TLC.WEBRip.AAC2.0.x264-BTW : title: Show Name season: 2 screen_size: 720p streaming_service: TLC source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: BTW type: episode # Streaming service: Investigation Discovery ? Show.Name.S01E01.720p.ID.WEBRip.AAC2.0.x264-BTW : title: Show Name season: 1 episode: 1 screen_size: 720p streaming_service: Investigation Discovery source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: BTW type: episode # Streaming service: RTE One ? Show.Name.S10E01.576p.RTE.WEBRip.AAC2.0.H.264-RTN : title: Show Name season: 10 episode: 1 screen_size: 576p streaming_service: RTE One source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: RTN type: episode # Streaming service: AMC ? Show.Name.S01E01.1080p.AMC.WEBRip.H.264.AAC2.0-CasStudio : title: Show Name season: 1 episode: 1 screen_size: 1080p streaming_service: AMC source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: CasStudio type: episode ? Suits.S07E01.1080p.iT.WEB-DL.DD5.1.H.264-VLAD.mkv ? Suits.S07E01.1080p.iTunes.WEB-DL.DD5.1.H.264-VLAD.mkv : title: Suits season: 7 episode: 1 screen_size: 1080p source: Web streaming_service: iTunes audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: VLAD container: mkv type: episode ? UpFront.S01.720p.AJAZ.WEBRip.AAC2.0.x264-BTW : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTW screen_size: 720p season: 1 source: Web streaming_service: Al Jazeera English title: UpFront type: episode video_codec: H.264 ? Smack.The.Pony.S01.4OD.WEBRip.AAC2.0.x264-BTW : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTW season: 1 source: Web streaming_service: Channel 4 title: Smack The Pony type: episode video_codec: H.264 ? Junior.Bake.Off.S06E03.1080p.ALL4.WEB-DL.AAC2.0.x264-NTb : title: Junior Bake Off season: 6 episode: 3 screen_size: 1080p streaming_service: Channel 4 source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: NTb type: episode ? The.Toy.Box.S01E01.720p.AMBC.WEBRip.AAC2.0.x264-BTN : audio_channels: '2.0' audio_codec: AAC episode: 1 other: Rip release_group: BTN screen_size: 720p season: 1 source: Web streaming_service: ABC title: The Toy Box type: episode video_codec: H.264 ? Gundam.Reconguista.in.G.S01.720p.ANLB.WEBRip.AAC2.0.x264-HorribleSubs : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: HorribleSubs screen_size: 720p season: 1 source: Web streaming_service: AnimeLab title: Gundam Reconguista in G type: episode video_codec: H.264 ? Animal.Nation.with.Anthony.Anderson.S01E01.1080p.ANPL.WEBRip.AAC2.0.x264-RTN : audio_channels: '2.0' audio_codec: AAC episode: 1 other: Rip release_group: RTN screen_size: 1080p season: 1 source: Web streaming_service: Animal Planet title: Animal Nation with Anthony Anderson type: episode video_codec: H.264 ? Park.Bench.S01.1080p.AOL.WEBRip.AAC2.0.H.264-BTW : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTW screen_size: 1080p season: 1 source: Web streaming_service: AOL title: Park Bench type: episode video_codec: H.264 ? Crime.Scene.Cleaner.S05.720p.ARD.WEBRip.AAC2.0.H.264-BTN : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTN screen_size: 720p season: 5 source: Web streaming_service: ARD title: Crime Scene Cleaner type: episode video_codec: H.264 ? Decker.S03.720p.AS.WEB-DL.AAC2.0.H.264-RTN : audio_channels: '2.0' audio_codec: AAC release_group: RTN screen_size: 720p season: 3 source: Web streaming_service: Adult Swim title: Decker type: episode video_codec: H.264 ? Southern.Charm.Savannah.S01E04.Hurricane.On.The.Horizon.1080p.BRAV.WEBRip.AAC2.0.x264-BTW : audio_channels: '2.0' audio_codec: AAC episode: 4 episode_title: Hurricane On The Horizon other: Rip release_group: BTW screen_size: 1080p season: 1 source: Web streaming_service: BravoTV title: Southern Charm Savannah type: episode video_codec: H.264 ? Four.in.the.Morning.S01E01.Pig.RERip.720p.CBC.WEBRip.AAC2.0.H.264-RTN : audio_channels: '2.0' audio_codec: AAC episode: 1 episode_title: Pig other: - Proper - Rip proper_count: 1 release_group: RTN screen_size: 720p season: 1 source: Web streaming_service: CBC title: Four in the Morning type: episode video_codec: H.264 ? Rio.Olympics.2016.08.07.Mens.Football.Group.C.Germany.vs.South.Korea.720p.CBC.WEBRip.AAC2.0.H.264-BTW : audio_channels: '2.0' audio_codec: AAC date: 2016-08-07 episode_title: Mens Football Group C Germany vs South Korea other: Rip release_group: BTW screen_size: 720p source: Web streaming_service: CBC title: Rio Olympics type: episode video_codec: H.264 ? Comedians.In.Cars.Getting.Coffee.S01.720p.CCGC.WEBRip.AAC2.0.x264-monkee : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: monkee screen_size: 720p season: 1 source: Web streaming_service: Comedians in Cars Getting Coffee title: Comedians In Cars Getting Coffee type: episode video_codec: H.264 ? Life.on.Top.S02.720p.CMAX.WEBRip.AAC2.0.x264-CMAX : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: CMAX screen_size: 720p season: 2 source: Web streaming_service: Cinemax title: Life on Top type: episode video_codec: H.264 ? Sun.Records.S01.720p.CMT.WEBRip.AAC2.0.x264-BTW : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTW screen_size: 720p season: 1 source: Web streaming_service: Country Music Television title: Sun Records type: episode video_codec: H.264 ? Infinity.Train.S01E00.Pilot.REPACK.720p.CN.WEBRip.AAC2.0.H.264-monkee : audio_channels: '2.0' audio_codec: AAC episode: 0 episode_details: Pilot episode_title: Pilot other: - Proper - Rip proper_count: 1 release_group: monkee screen_size: 720p season: 1 source: Web streaming_service: Cartoon Network title: Infinity Train type: episode video_codec: H.264 ? Jay.Lenos.Garage.2015.S03E02.1080p.CNBC.WEB-DL.x264-TOPKEK : episode: 2 release_group: TOPKEK screen_size: 1080p season: 3 source: Web streaming_service: CNBC title: Jay Lenos Garage type: episode video_codec: H.264 year: 2015 ? US.Presidential.Debates.2015.10.28.Third.Republican.Debate.720p.CNBC.WEBRip.AAC2.0.H.264-monkee : audio_channels: '2.0' audio_codec: AAC country: US date: 2015-10-28 episode_title: Third Republican Debate other: Rip release_group: monkee screen_size: 720p source: Web streaming_service: CNBC title: Presidential Debates type: episode video_codec: H.264 ? What.The.Fuck.France.S01E01.Le.doublage.CNLP.WEBRip.AAC2.0.x264-TURTLE : audio_channels: '2.0' audio_codec: AAC episode: 1 episode_title: Le doublage other: Rip release_group: TURTLE season: 1 source: Web streaming_service: Canal+ title: What The Fuck France type: episode video_codec: H.264 ? SuperMansion.S02.720p.CRKL.WEBRip.AAC2.0.x264-VLAD : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: VLAD screen_size: 720p season: 2 source: Web streaming_service: Crackle title: SuperMansion type: episode video_codec: H.264 ? Chosen.S02.1080p.CRKL.WEBRip.AAC2.0.x264-AJP69 : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: AJP69 screen_size: 1080p season: 2 source: Web streaming_service: Crackle title: Chosen type: episode video_codec: H.264 ? Chosen.S03.1080p.CRKL.WEBRip.AAC2.0.x264-AJP69 : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: AJP69 screen_size: 1080p season: 3 source: Web streaming_service: Crackle title: Chosen type: episode video_codec: H.264 ? Snatch.S01.1080p.CRKL.WEBRip.AAC2.0.x264-DEFLATE : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: DEFLATE screen_size: 1080p season: 1 source: Web streaming_service: Crackle title: Snatch type: episode video_codec: H.264 ? White.House.Correspondents.Dinner.2015.Complete.CSPN.WEBRip.AAC2.0.H.264-BTW : audio_channels: '2.0' audio_codec: AAC other: - Complete - Rip release_group: BTW source: Web streaming_service: CSpan title: White House Correspondents Dinner type: movie video_codec: H.264 year: 2015 ? The.Amazing.Race.Canada.S03.720p.CTV.WEBRip.AAC2.0.H.264-BTW : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTW screen_size: 720p season: 3 source: Web streaming_service: CTV title: The Amazing Race Canada type: episode video_codec: H.264 ? Miniverse.S01E01.Explore.the.Solar.System.2160p.CUR.WEB-DL.DDP2.0.x264-monkee : audio_channels: '2.0' audio_codec: Dolby Digital Plus episode: 1 episode_title: Explore the Solar System release_group: monkee screen_size: 2160p season: 1 source: Web streaming_service: CuriosityStream title: Miniverse type: episode video_codec: H.264 ? Vixen.S02.720p.CWS.WEBRip.AAC2.0.x264-BMF : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BMF screen_size: 720p season: 2 source: Web streaming_service: CWSeed title: Vixen type: episode video_codec: H.264 ? Abidin.Dino.DDY.WEBRip.AAC2.0.H.264-BTN : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTN source: Web streaming_service: Digiturk Diledigin Yerde title: Abidin Dino type: movie video_codec: H.264 ? Fast.N.Loud.S08.1080p.DISC.WEBRip.AAC2.0.x264-RTN : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: RTN screen_size: 1080p season: 8 source: Web streaming_service: Discovery title: Fast N Loud type: episode video_codec: H.264 ? Bake.Off.Italia.S04.1080p.DPLY.WEBRip.AAC2.0.x264-Threshold : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: Threshold screen_size: 1080p season: 4 source: Web streaming_service: DPlay title: Bake Off Italia type: episode video_codec: H.264 ? Long.Riders.S01.DSKI.WEBRip.AAC2.0.x264-HorribleSubs : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: HorribleSubs season: 1 source: Web streaming_service: Daisuki title: Long Riders type: episode video_codec: H.264 ? Milo.Murphys.Law.S01.720p.DSNY.WEB-DL.AAC2.0.x264-TVSmash : audio_channels: '2.0' audio_codec: AAC release_group: TVSmash screen_size: 720p season: 1 source: Web streaming_service: Disney title: Milo Murphys Law type: episode video_codec: H.264 ? 30.for.30.S03E15.Doc.and.Darryl.720p.ESPN.WEBRip.AAC2.0.x264-BTW : audio_channels: '2.0' audio_codec: AAC episode: 15 episode_title: Doc and Darryl other: Rip release_group: BTW screen_size: 720p season: 3 source: Web streaming_service: ESPN title: 30 for 30 type: episode video_codec: H.264 ? Boundless.S03.720p.ESQ.WEBRip.AAC2.0.x264-RTN : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: RTN screen_size: 720p season: 3 source: Web streaming_service: Esquire title: Boundless type: episode video_codec: H.264 ? Periodismo.Para.Todos.S2016E01.720p.ETTV.WEBRip.AAC2.0.H.264-braggart74 : audio_channels: '2.0' audio_codec: AAC episode: 1 other: Rip release_group: braggart74 screen_size: 720p season: 2016 source: Web streaming_service: El Trece title: Periodismo Para Todos type: episode video_codec: H.264 year: 2016 ? Just.Jillian.S01E01.1080p.ETV.WEBRip.AAC2.0.x264-GoApe : audio_channels: '2.0' audio_codec: AAC episode: 1 other: Rip release_group: GoApe screen_size: 1080p season: 1 source: Web streaming_service: E! title: Just Jillian type: episode video_codec: H.264 ? New.Money.S01.1080p.ETV.WEBRip.AAC2.0.x264-BTW : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTW screen_size: 1080p season: 1 source: Web streaming_service: E! title: New Money type: episode video_codec: H.264 ? Gaming.Show.In.My.Parents.Garage.S02E01.The.Power.Up1000.FAM.WEBRip.AAC2.0.x264-RTN : audio_channels: '2.0' audio_codec: AAC episode: 1 episode_title: The Power Up1000 other: Rip release_group: RTN season: 2 source: Web streaming_service: Family title: Gaming Show In My Parents Garage type: episode video_codec: H.264 ? Little.People.2016.S01E03.Proud.to.Be.You.and.Me.720p.FJR.WEBRip.AAC2.0.x264-RTN : audio_channels: '2.0' audio_codec: AAC episode: 3 episode_title: Proud to Be You and Me other: Rip release_group: RTN screen_size: 720p season: 1 source: Web streaming_service: Family Jr title: Little People type: episode video_codec: H.264 year: 2016 ? The.Pioneer.Woman.S00E08.Summer.Summer.Summer.720p.FOOD.WEB-DL.AAC2.0.x264-AJP69 : audio_channels: '2.0' audio_codec: AAC episode: 8 episode_title: Summer Summer Summer release_group: AJP69 screen_size: 720p season: 0 source: Web streaming_service: Food Network title: The Pioneer Woman type: episode video_codec: H.264 ? Prata.da.Casa.S01E01.720p.FOX.WEBRip.AAC2.0.H.264-BARRY : audio_channels: '2.0' audio_codec: AAC episode: 1 other: Rip release_group: BARRY screen_size: 720p season: 1 source: Web streaming_service: Fox title: Prata da Casa type: episode video_codec: H.264 ? Grandfathered.S01.720p.FOX.WEBRip.AAC2.0.H.264-BTW : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTW screen_size: 720p season: 1 source: Web streaming_service: Fox title: Grandfathered type: episode video_codec: H.264 ? Truth.and.Iliza.S01E01.FREE.WEBRip.AAC2.0.x264-BTN : audio_channels: '2.0' audio_codec: AAC episode: 1 other: Rip release_group: BTN season: 1 source: Web streaming_service: Freeform title: Truth and Iliza type: episode video_codec: H.264 ? Seven.Year.Switch.S01.720p.FYI.WEBRip.AAC2.0.x264-BTW : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTW screen_size: 720p season: 1 source: Web streaming_service: FYI Network title: Seven Year Switch type: episode video_codec: H.264 ? NHL.2015.10.09.Leafs.vs.Red.Wings.Condensed.Game.720p.Away.Feed.GC.WEBRip.AAC2.0.H.264-BTW : audio_channels: '2.0' audio_codec: AAC date: 2015-10-09 episode_title: Leafs vs Red Wings Condensed Game other: Rip release_group: BTW screen_size: 720p source: Web streaming_service: NHL GameCenter title: NHL type: episode video_codec: H.264 ? NHL.2016.01.26.Maple.Leafs.vs.Panthers.720p.Home.Feed.GC.WEBRip.AAC2.0.H.264-BTW : audio_channels: '2.0' audio_codec: AAC date: 2016-01-26 episode_title: Maple Leafs vs Panthers other: Rip release_group: BTW screen_size: 720p source: Web streaming_service: NHL GameCenter title: NHL type: episode video_codec: H.264 ? Big.Brother.Canada.S05.GLBL.WEBRip.AAC2.0.H.264-RTN : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: RTN season: 5 source: Web streaming_service: Global title: Big Brother Canada type: episode video_codec: H.264 ? Pornolandia.S01.720p.GLOB.WEBRip.AAC2.0.x264-GeneX : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: GeneX screen_size: 720p season: 1 source: Web streaming_service: GloboSat Play title: Pornolandia type: episode video_codec: H.264 ? Transando.com.Laerte.S01.720p.GLOB.WEBRip.AAC2.0.x264-GeneX : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: GeneX screen_size: 720p season: 1 source: Web streaming_service: GloboSat Play title: Transando com Laerte type: episode video_codec: H.264 ? Flip.or.Flop.S01.720p.HGTV.WEBRip.AAC2.0.H.264-AJP69 : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: AJP69 screen_size: 720p season: 1 source: Web streaming_service: HGTV title: Flip or Flop type: episode video_codec: H.264 ? Kitten.Bowl.2014.720p.HLMK.WEBRip.AAC2.0.x264-monkee : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: monkee screen_size: 720p source: Web streaming_service: Hallmark title: Kitten Bowl type: movie video_codec: H.264 year: 2014 ? Still.Star-Crossed.S01E05.720p.HULU.WEB-DL.AAC2.0.H.264-VLAD : audio_channels: '2.0' audio_codec: AAC episode: 5 release_group: VLAD screen_size: 720p season: 1 source: Web streaming_service: Hulu title: Still Star-Crossed type: episode video_codec: H.264 ? EastEnders.2017.07.17.720p.iP.WEB-DL.AAC2.0.H.264-BTN : audio_channels: '2.0' audio_codec: AAC date: 2017-07-17 release_group: BTN screen_size: 720p source: Web streaming_service: BBC iPlayer title: EastEnders type: episode video_codec: H.264 ? Handmade.in.Japan.S01E01.720p.iP.WEBRip.AAC2.0.H.264-SUP : audio_channels: '2.0' audio_codec: AAC episode: 1 other: Rip release_group: SUP screen_size: 720p season: 1 source: Web streaming_service: BBC iPlayer title: Handmade in Japan type: episode video_codec: H.264 ? The.Chillenden.Murders.S01.720p.iP.WEBRip.AAC2.0.H.264-HAX : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: HAX screen_size: 720p season: 1 source: Web streaming_service: BBC iPlayer title: The Chillenden Murders type: episode video_codec: H.264 ? The.Street.S01.ITV.WEB-DL.AAC2.0.x264-RTN : audio_channels: '2.0' audio_codec: AAC release_group: RTN season: 1 source: Web streaming_service: ITV title: The Street type: episode video_codec: H.264 ? Hope.for.Wildlife.S04.1080p.KNOW.WEBRip.AAC2.0.x264-BTW : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTW screen_size: 1080p season: 4 source: Web streaming_service: Knowledge Network title: Hope for Wildlife type: episode video_codec: H.264 ? Kim.of.Queens.S02.720p.LIFE.WEBRip.AAC2.0.H.264-RTN : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: RTN screen_size: 720p season: 2 source: Web streaming_service: Lifetime title: Kim of Queens type: episode video_codec: H.264 ? The.Rachel.Maddow.Show.2017.02.22.720p.MNBC.WEBRip.AAC2.0.x264-BTW : audio_channels: '2.0' audio_codec: AAC date: 2017-02-22 other: Rip release_group: BTW screen_size: 720p source: Web streaming_service: MSNBC title: The Rachel Maddow Show type: episode video_codec: H.264 ? Ignition.S06E12.720p.MTOD.WEB-DL.AAC2.0.x264-RTN : audio_channels: '2.0' audio_codec: AAC episode: 12 release_group: RTN screen_size: 720p season: 6 source: Web streaming_service: Motor Trend OnDemand title: Ignition type: episode video_codec: H.264 ? Teen.Mom.UK.S01E01.Life.as.a.Teen.Mum.1080p.MTV.WEB-DL.AAC2.0.x264-BTW : audio_channels: '2.0' audio_codec: AAC country: GB episode: 1 episode_title: Life as a Teen Mum release_group: BTW screen_size: 1080p season: 1 source: Web streaming_service: MTV title: Teen Mom type: episode video_codec: H.264 ? Undrafted.S01.720p.NFLN.WEBRip.AAC2.0.H.264-TTYL : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: TTYL screen_size: 720p season: 1 source: Web streaming_service: NFL Now title: Undrafted type: episode video_codec: H.264 ? NFL.2016.08.25.PreSeason.Cowboys.vs.Seahawks.720p.NFL.WEBRip.AAC2.0.H.264-BTW : audio_channels: '2.0' audio_codec: AAC date: 2016-08-25 episode_title: PreSeason Cowboys vs Seahawks other: Rip release_group: BTW screen_size: 720p source: Web streaming_service: NFL title: NFL type: episode video_codec: H.264 ? Bunsen.is.a.Beast.S01E23.Guinea.Some.Lovin.1080p.NICK.WEBRip.AAC2.0.x264-TVSmash : audio_channels: '2.0' audio_codec: AAC episode: 23 episode_title: Guinea Some Lovin other: Rip release_group: TVSmash screen_size: 1080p season: 1 source: Web streaming_service: Nickelodeon title: Bunsen is a Beast type: episode video_codec: H.264 ? Valkyrie.S01.720p.NRK.WEBRip.AAC2.0.x264-BTN : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTN screen_size: 720p season: 1 source: Web streaming_service: Norsk Rikskringkasting title: Valkyrie type: episode video_codec: H.264 ? Food.Forward.S01.720p.PBS.WEBRip.AAC2.0.x264-RTN : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: RTN screen_size: 720p season: 1 source: Web streaming_service: PBS title: Food Forward type: episode video_codec: H.264 ? SciGirls.S01E01.Turtle.Mania.720p.PBSK.WEBRip.AAC2.0.x264-RTN : audio_channels: '2.0' audio_codec: AAC episode: 1 episode_title: Turtle Mania other: Rip release_group: RTN screen_size: 720p season: 1 source: Web streaming_service: PBS Kids title: SciGirls type: episode video_codec: H.264 ? Powers.2015.S01.1080p.PSN.WEBRip.DD5.1.x264-NTb : audio_channels: '5.1' audio_codec: Dolby Digital other: Rip release_group: NTb screen_size: 1080p season: 1 source: Web streaming_service: Playstation Network title: Powers type: episode video_codec: H.264 year: 2015 ? Escape.The.Night.S02E02.The.Masquerade.Part.II.1080p.RED.WEBRip.AAC5.1.VP9-BTW : audio_channels: '5.1' audio_codec: AAC episode: 2 episode_title: The Masquerade other: Rip part: 2 release_group: BTW screen_size: 1080p season: 2 source: Web streaming_service: YouTube Red title: Escape The Night type: episode video_codec: VP9 ? Escape.The.Night.S02E02.The.Masquerade.Part.II.2160p.RED.WEBRip.AAC5.1.VP9-BTW : audio_channels: '5.1' audio_codec: AAC episode: 2 episode_title: The Masquerade other: Rip part: 2 release_group: BTW screen_size: 2160p season: 2 source: Web streaming_service: YouTube Red title: Escape The Night type: episode video_codec: VP9 ? Escape.The.Night.S02E02.The.Masquerade.Part.II.720p.RED.WEBRip.AAC5.1.VP9-BTW : audio_channels: '5.1' audio_codec: AAC episode: 2 episode_title: The Masquerade other: Rip part: 2 release_group: BTW screen_size: 720p season: 2 source: Web streaming_service: YouTube Red title: Escape The Night type: episode video_codec: VP9 ? The.Family.Law.S02E01.720p.SBS.WEB-DL.AAC2.0.H.264-BTN : audio_channels: '2.0' audio_codec: AAC episode: 1 release_group: BTN screen_size: 720p season: 2 source: Web streaming_service: SBS title: The Family Law type: episode video_codec: H.264 ? Theres.No.Joy.In.Beachville.The.True.Story.of.Baseballs.Origin.720p.SNET.WEBRip.AAC2.0.x264-BTW : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTW screen_size: 720p source: Web streaming_service: Sportsnet title: Theres No Joy In Beachville The True Story of Baseballs Origin type: movie video_codec: H.264 ? One.Night.Only.Alec.Baldwin.720p.SPIK.WEB-DL.AAC2.0.x264-NOGRP : audio_channels: '2.0' audio_codec: AAC release_group: NOGRP screen_size: 720p source: Web streaming_service: Spike title: One Night Only Alec Baldwin type: movie video_codec: H.264 ? Ink.Master.S08.720p.SPIK.WEBRip.AAC2.0.x264-BTW : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTW screen_size: 720p season: 8 source: Web streaming_service: Spike title: Ink Master type: episode video_codec: H.264 ? Jungle.Bunch.S01E01.Deep.Chasm.1080p.SPRT.WEBRip.AAC2.0.x264-RTN : audio_channels: '2.0' audio_codec: AAC episode: 1 episode_title: Deep Chasm other: Rip release_group: RTN screen_size: 1080p season: 1 source: Web streaming_service: Sprout title: Jungle Bunch type: episode video_codec: H.264 ? Ash.vs.Evil.Dead.S01.720p.STZ.WEBRip.AAC2.0.x264-NTb : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: NTb screen_size: 720p season: 1 source: Web streaming_service: Starz title: Ash vs Evil Dead type: episode video_codec: H.264 ? WWE.Swerved.S01.720p.WWEN.WEBRip.AAC2.0.H.264-PPKORE : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: PPKORE screen_size: 720p season: 1 source: Web streaming_service: WWE Network title: WWE Swerved type: episode video_codec: H.264 ? Face.Off.S11.1080p.SYFY.WEBRip.AAC2.0.x264-BTW : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTW screen_size: 1080p season: 11 source: Web streaming_service: Syfy title: Face Off type: episode video_codec: H.264 ? Conan.2016.09.22.Jeff.Garlin.720p.TBS.WEBRip.AAC2.0.H.264-NOGRP : audio_channels: '2.0' audio_codec: AAC date: 2016-09-22 episode_title: Jeff Garlin other: Rip release_group: NOGRP screen_size: 720p source: Web streaming_service: TBS title: Conan type: episode video_codec: H.264 ? Swans.Crossing.S01.TUBI.WEBRip.AAC2.0.x264-RTN : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: RTN season: 1 source: Web streaming_service: TubiTV title: Swans Crossing type: episode video_codec: H.264 ? The.Joy.of.Techs.S01.UKTV.WEB-DL.AAC2.0.x264-RTN : audio_channels: '2.0' audio_codec: AAC release_group: RTN season: 1 source: Web streaming_service: UKTV title: The Joy of Techs type: episode video_codec: H.264 ? Rock.Icons.S01.720p.VH1.WEB-DL.AAC2.0.H.264-RTN : audio_channels: '2.0' audio_codec: AAC release_group: RTN screen_size: 720p season: 1 source: Web streaming_service: VH1 title: Rock Icons type: episode video_codec: H.264 ? Desus.and.Mero.S01E130.2017.07.18.1080p.VICE.WEB-DL.AAC2.0.x264-RTN : audio_channels: '2.0' audio_codec: AAC date: 2017-07-18 episode: 130 release_group: RTN screen_size: 1080p season: 1 source: Web streaming_service: Viceland title: Desus and Mero type: episode video_codec: H.264 ? Graveyard.Carz.S07.1080p.VLCT.WEBRip.AAC2.0.x264-RTN : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: RTN screen_size: 1080p season: 7 source: Web streaming_service: Velocity title: Graveyard Carz type: episode video_codec: H.264 ? Other.Space.S01E01.1080p.YHOO.WEBRip.AAC2.0.x264-BTW : audio_channels: '2.0' audio_codec: AAC episode: 1 other: Rip release_group: BTW screen_size: 1080p season: 1 source: Web streaming_service: Yahoo title: Other Space type: episode video_codec: H.264 ? Americas.Test.Kitchen.S17.720p.ATK.WEB-DL.AAC2.0.x264-BTN : audio_channels: '2.0' audio_codec: AAC release_group: BTN screen_size: 720p season: 17 source: Web streaming_service: America's Test Kitchen title: Americas Test Kitchen type: episode video_codec: H.264 ? Bushwhacked.Bugs.S01.AUBC.WEBRip.AAC2.0.H.264-DAWN : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: DAWN season: 1 source: Web streaming_service: ABC Australia title: Bushwhacked Bugs type: episode video_codec: H.264 ? VICE.S05E12.1080p.HBO.WEB-DL.AAC2.0.H.264-monkee ? VICE.S05E12.1080p.HBO-Go.WEB-DL.AAC2.0.H.264-monkee ? VICE.S05E12.1080p.HBOGo.WEB-DL.AAC2.0.H.264-monkee : audio_channels: '2.0' audio_codec: AAC episode: 12 release_group: monkee screen_size: 1080p season: 5 source: Web streaming_service: HBO Go title: VICE type: episode video_codec: H.264 ? Dix.Pour.Cent.S02.PLUZ.WEBRip.AAC2.0.H.264-TURTLE : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: TURTLE season: 2 source: Web streaming_service: Pluzz title: Dix Pour Cent type: episode video_codec: H.264 ? Ulveson.och.Herngren.S01.720p.SVT.WEBRip.AAC2.0.H.264-BTN : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTN screen_size: 720p season: 1 source: Web streaming_service: Sveriges Television title: Ulveson och Herngren type: episode video_codec: H.264 ? Bravest.Warriors.S03.1080p.VRV.WEBRip.AAC2.0.x264-BTN : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTN screen_size: 1080p season: 3 source: Web streaming_service: VRV title: Bravest Warriors type: episode video_codec: H.264 ? The.Late.Night.Big.Breakfast.S02.WME.WEBRip.AAC2.0.x264-BTN : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTN season: 2 source: Web streaming_service: WatchMe title: The Late Night Big Breakfast type: episode video_codec: H.264 ? Hockey.Wives.S02.WNET.WEBRip.AAC2.0.H.264-BTW : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTW season: 2 source: Web streaming_service: W Network title: Hockey Wives type: episode video_codec: H.264 ? Sin.City.Saints.S01.1080p.YHOO.WEBRip.AAC2.0.x264-NTb : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: NTb screen_size: 1080p season: 1 source: Web streaming_service: Yahoo title: Sin City Saints type: episode video_codec: H.264 ? 555.S01.1080p.VMEO.WEBRip.AAC2.0.x264-BTN : audio_channels: '2.0' audio_codec: AAC other: Rip release_group: BTN screen_size: 1080p season: 1 source: Web streaming_service: Vimeo # title: '555' type: episode video_codec: H.264 ? Good.Bones.S02E01.Decaying.Duplex.Transformed.720p.9NOW.WEB-DL.AAC2.0.x264-RTN : title: Good Bones season: 2 episode: 1 episode_title: Decaying Duplex Transformed screen_size: 720p streaming_service: 9Now source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: RTN type: episode ? Satisfaction.S01E01.Running.Girl.1080p.BNGE.WEB-DL.DD5.1.H.264-NTb : title: Satisfaction season: 1 episode: 1 episode_title: Running Girl screen_size: 1080p streaming_service: Binge source: Web audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: NTb type: episode ? Virgin.S01E01.Birthday.Party.1440p.BKPL.WEB-DL.H.264-LiGHT : title: Virgin season: 1 episode: 1 episode_title: Birthday Party screen_size: 1440p streaming_service: Blackpills source: Web video_codec: H.264 release_group: LiGHT type: episode ? Ciplak S01E05 1080p BLU WEBRip AAC2.0 H.264 TURG : title: Ciplak season: 1 episode: 5 screen_size: 1080p streaming_service: BluTV source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: TURG type: episode ? Scooby.Doo.and.Guess.Who.S01E01.Revenge.of.the.Swamp.Monster.1080p.BOOM.WEB-DL.AAC2.0.H.264-QOQ : title: Scooby Doo and Guess Who season: 1 episode: 1 episode_title: Revenge of the Swamp Monster screen_size: 1080p streaming_service: Boomerang source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: QOQ type: episode ? The.Beaverton.S01E01.1080p.CRAV.WEB-DL.DD5.1.H.264-NTb : title: The Beaverton season: 1 episode: 1 screen_size: 1080p streaming_service: Crave source: Web audio_codec: Dolby Digital audio_channels: '5.1' video_codec: H.264 release_group: NTb type: episode ? Gold.Rush.Freddy.Dodges.Mine.Rescue.S01E06.Sink.or.Swim.1080p.DSCP.WEB-DL.AAC2.0.X264-RTN : title: Gold Rush Freddy Dodges Mine Rescue season: 1 episode: 6 episode_title: Sink or Swim screen_size: 1080p streaming_service: Discovery Plus source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: RTN type: episode ? The.Mandalorian.S01E01.Chapter.1.2160p.DSNP.WEB-DL.DDP5.1.Atmos.DV.HEVC-MZABI : title: The Mandalorian season: 1 episode: 1 episode_title: Chapter 1 screen_size: 2160p streaming_service: Disney+ source: Web audio_codec: - Dolby Digital Plus - Dolby Atmos audio_channels: '5.1' video_codec: 'H.265' release_group: 'MZABI' type: episode ? Sorry.For.Your.Loss.S01E01.One.Fun.Thing.1080p.FBWatch.WEB-DL.AAC2.0.x264-SAMUEL98 : title: Sorry For Your Loss season: 1 episode: 1 episode_title: One Fun Thing screen_size: 1080p streaming_service: Facebook Watch source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: SAMUEL98 type: episode ? Heartland.1979.720p.FANDOR.WEB-DL.AAC2.0.H.264-Cinefeel : title: Heartland year: 1979 screen_size: 720p streaming_service: Fandor source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: Cinefeel ? Rio.Heroes.S01E01.Sem.Regras.720p.FOXP.WEB-DL.AAC2.0.x264-BTW : title: Rio Heroes season: 1 episode: 1 episode_title: Sem Regras screen_size: 720p streaming_service: Fox Premium source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: BTW type: episode ? Deadline.Gallipoli.S01E01.1080p.FXTL.WEB-DL.DDP5.1.H.264-NTb : title: Deadline Gallipoli season: 1 episode: 1 screen_size: 1080p streaming_service: Foxtel source: Web audio_codec: Dolby Digital Plus audio_channels: '5.1' video_codec: 'H.264' release_group: NTb type: episode ? My Bromance 2 - 5 Years Later S01E01 1080p Gaga WEB-DL AAC2.0 H.264-FRE3LOV : title: My Bromance 2 alternative_title: 5 Years Later season: 1 episode: 1 screen_size: 1080p streaming_service: GagaOOLala source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: FRE3LOV type: episode ? Cheeni.2020.1080p.HoiChoi.WEB-DL.AAC.2.0.H.264-iDC : title: Cheeni year: 2020 screen_size: 1080p streaming_service: hoichoi source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: iDC type: movie ? Surau.dan.Silek.2017.720p.IFX.WEB-DL.AAC.x264-RsA : title: Surau dan Silek year: 2017 screen_size: 720p streaming_service: iflix source: Web audio_codec: AAC video_codec: H.264 release_group: RsA type: movie ? She.Would.Never.Know.S01E10.1080p.iQIYI.WEB-DL.AAC.2.0.x265-SH3LBY : title: She Would Never Know season: 1 episode: 10 screen_size: 1080p streaming_service: iQIYI source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.265 release_group: SH3LBY type: episode ? Direktoren.for.det.hele.2006.SUBBED.1080p.MUBI.WEB-DL.AAC2.0.H264-CMYK : title: Direktoren for det hele year: 2006 screen_size: 1080p streaming_service: MUBI source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: CMYK type: movie ? Les.trois.couronnes.du.matelot.1983.1080p.INA.WEB-DL.AAC2.0.x264-KG : title: Les trois couronnes du matelot year: 1983 screen_size: 1080p streaming_service: National Audiovisual Institute source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: KG type: movie ? Caninabis.Junkie.Dog.1979.480p.NFB.WEB-DL.AAC2.0.H.264-QiQ : title: Caninabis Junkie Dog year: 1979 screen_size: 480p streaming_service: National Film Board source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: QiQ type: movie ? First.Ladies.S01E04.1080p.OPTO.WEB-DL.H264-CKTV : title: First Ladies season: 1 episode: 4 screen_size: 1080p streaming_service: Opto source: Web video_codec: H.264 release_group: CKTV type: episode ? Queen.Sugar.S05E01.720p.OWN.WEB-DL.AAC2.0.H.264-BTN : title: Queen Sugar season: 5 episode: 1 screen_size: 720p streaming_service: Oprah Winfrey Network source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: BTN type: episode ? Saved.by.the.Bell.2020.S01E01.Pilot.1080p.PCOK.WEB-DL.DDP5.1.x264-NTb : title: Saved by the Bell year: 2020 season: 1 episode: 1 episode_title: Pilot screen_size: 1080p streaming_service: Peacock source: Web audio_codec: Dolby Digital Plus audio_channels: '5.1' video_codec: H.264 release_group: NTb type: episode ? Where's.Waldo.2019.S01E01.1080p.Peacock.WEB_DL.DD+5.1.H.264-SH3LBY : title: Where's Waldo year: 2019 season: 1 episode: 1 screen_size: 1080p streaming_service: Peacock source: Web audio_codec: Dolby Digital Plus audio_channels: '5.1' video_codec: H.264 release_group: SH3LBY type: episode ? High.Stakes.Poker.S08E01.1080p.POGO.WEB-DL.AAC2.0.H.264-RAiSY : title: High Stakes Poker season: 8 episode: 1 screen_size: 1080p streaming_service: PokerGO source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: RAiSY type: episode ? Into.the.Darkness.2020.HDR.2160.RKTN.WEB-DL.x265-ROCCaT : title: Into the Darkness year: 2020 other: HDR10 screen_size: 2160p streaming_service: Rakuten TV source: Web video_codec: H.265 release_group: ROCCaT type: movie ? Cold.Case.S01E01.Look.Again.1080p.ROKU.WEB-DL.AAC2.0.H.264-ETHiCS : title: Cold Case season: 1 episode: 1 episode_title: Look Again screen_size: 1080p streaming_service: The Roku Channel source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: ETHiCS type: episode ? Valmentaja.2018.720p.RUUTU.WEB-DL.AAC2.0.x264-D3 : title: Valmentaja year: 2018 screen_size: 720p streaming_service: RUUTU source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: D3 type: movie ? Mysteries.of.the.Abandoned.S07E10.Mystery.of.the.Alien.Base.720p.SCI.WEBRip.AAC2.0.x264-BOOP : title: Mysteries of the Abandoned season: 7 episode: 10 episode_title: Mystery of the Alien Base screen_size: 720p streaming_service: Science Channel source: Web other: Rip audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: BOOP type: episode ? Powers (2015) S01E01 Pilot 2160p Sony WEBRip DTS-HD MA 5.1 x264-TrollUHD : title: Powers year: 2015 season: 1 episode: 1 episode_title: Pilot screen_size: 2160p streaming_service: Sony source: Web other: Rip audio_codec: DTS-HD audio_profile: Master Audio audio_channels: '5.1' video_codec: H.264 release_group: TrollUHD type: episode ? Wellington.Paranormal.S02E01.Taniwha.1080p.TVNZ.WEB-DL.AAC2.0.H.264-Dulus : title: Wellington Paranormal season: 2 episode: 1 episode_title: Taniwha screen_size: 1080p streaming_service: TVNZ source: Web audio_codec: AAC audio_channels: '2.0' video_codec: H.264 release_group: Dulus type: episode ? UFC.Fight.Night.185.Prelims.FP.WEB-DL.H264-SHREDDiE : title: UFC Fight Night # NOTE: E185 exceeds the default configuration of E100 being the max. season: 1 episode: 85 episode_title: Prelims streaming_service: UFC Fight Pass source: Web video_codec: H.264 release_group: SHREDDiE type: episode # All this below shouldn't match any streaming services ? London.2012.Olympics.CTV.Preview.Show.HDTV.x264-2HD : alternative_title: Olympics CTV Preview Show release_group: 2HD source: HDTV title: London type: movie video_codec: H.264 year: 2012 ? UFC.on.FOX.24.1080p.HDTV.x264-VERUM : episode: 24 release_group: VERUM screen_size: 1080p source: HDTV title: UFC on FOX type: episode video_codec: H.264 ? ESPN.E.60.2016.10.04.HDTV.x264-LoTV : date: 2016-10-04 episode: 60 release_group: LoTV source: HDTV title: ESPN E type: episode video_codec: H.264 ? GTTV.E3.All.Access.Live.Day.1.Xbox.Showcase.Preshow.HDTV.x264-SYS : episode: 3 episode_title: All Access Live Day 1 Xbox Showcase Preshow release_group: SYS source: HDTV title: GTTV type: episode video_codec: H.264 guessit-3.8.0/guessit/test/suggested.json000066400000000000000000000005701453655371700205560ustar00rootroot00000000000000{ "titles": [ "13 Reasons Why", "Star Wars: Episode VII - The Force Awakens", "3%", "The 100", "3 Percent", "This is Us", "Open Season 2", "Game of Thrones", "The X-Files", "11.22.63" ], "suggested": [ "13 Reasons Why", "Star Wars: Episode VII - The Force Awakens", "The 100", "Open Season 2", "11.22.63" ] }guessit-3.8.0/guessit/test/test-input-file.txt000066400000000000000000000001311453655371700214540ustar00rootroot00000000000000Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv SecondFile.aviguessit-3.8.0/guessit/test/test_api.py000066400000000000000000000073771453655371700200670ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=pointless-statement, missing-docstring, invalid-name, pointless-string-statement import json import os from pathlib import Path import pytest from pytest_mock import MockerFixture from .. import api from ..api import guessit, properties, suggested_expected, GuessitException, default_api __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) def test_default(): ret = guessit('Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') assert ret and 'title' in ret def test_forced_unicode(): ret = guessit('Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') assert ret and 'title' in ret and isinstance(ret['title'], str) def test_forced_binary(): ret = guessit(b'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') assert ret and 'title' in ret and isinstance(ret['title'], bytes) def test_pathlike_object(): path = Path('Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') ret = guessit(path) assert ret and 'title' in ret def test_unicode_japanese(): ret = guessit('[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi') assert ret and 'title' in ret def test_unicode_japanese_options(): ret = guessit("[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi", options={"expected_title": ["阿维达"]}) assert ret and 'title' in ret and ret['title'] == "阿维达" def test_forced_unicode_japanese_options(): ret = guessit("[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi", options={"expected_title": ["阿维达"]}) assert ret and 'title' in ret and ret['title'] == "阿维达" def test_properties(): props = properties() assert 'video_codec' in props.keys() def test_exception(): with pytest.raises(GuessitException) as excinfo: guessit(object()) assert "An internal error has occurred in guessit" in str(excinfo.value) assert "Guessit Exception Report" in str(excinfo.value) assert "Please report at https://github.com/guessit-io/guessit/issues" in str(excinfo.value) def test_suggested_expected(): with open(os.path.join(__location__, 'suggested.json'), 'r', encoding='utf-8') as f: content = json.load(f) actual = suggested_expected(content['titles']) assert actual == content['suggested'] def test_should_rebuild_rebulk_on_advanced_config_change(mocker: MockerFixture): api.reset() rebulk_builder_spy = mocker.spy(api, 'rebulk_builder') string = "some.movie.trfr.mkv" result1 = default_api.guessit(string) assert result1.get('title') == 'some movie trfr' assert 'subtitle_language' not in result1 rebulk_builder_spy.assert_called_once_with(mocker.ANY) rebulk_builder_spy.reset_mock() result2 = default_api.guessit(string, {'advanced_config': {'language': {'subtitle_prefixes': ['tr']}}}) assert result2.get('title') == 'some movie' assert str(result2.get('subtitle_language')) == 'fr' rebulk_builder_spy.assert_called_once_with(mocker.ANY) rebulk_builder_spy.reset_mock() def test_should_not_rebuild_rebulk_on_same_advanced_config(mocker: MockerFixture): api.reset() rebulk_builder_spy = mocker.spy(api, 'rebulk_builder') string = "some.movie.subfr.mkv" result1 = default_api.guessit(string) assert result1.get('title') == 'some movie' assert str(result1.get('subtitle_language')) == 'fr' rebulk_builder_spy.assert_called_once_with(mocker.ANY) rebulk_builder_spy.reset_mock() result2 = default_api.guessit(string) assert result2.get('title') == 'some movie' assert str(result2.get('subtitle_language')) == 'fr' assert rebulk_builder_spy.call_count == 0 rebulk_builder_spy.reset_mock() guessit-3.8.0/guessit/test/test_api_unicode_literals.py000066400000000000000000000044071453655371700234630ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=pointless-statement, missing-docstring, invalid-name, pointless-string-statement import os import pytest from ..api import guessit, properties, GuessitException __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) def test_default(): ret = guessit('Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') assert ret and 'title' in ret def test_forced_unicode(): ret = guessit('Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') assert ret and 'title' in ret and isinstance(ret['title'], str) def test_forced_binary(): ret = guessit(b'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') assert ret and 'title' in ret and isinstance(ret['title'], bytes) def test_unicode_japanese(): ret = guessit('[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi') assert ret and 'title' in ret def test_unicode_japanese_options(): ret = guessit("[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi", options={"expected_title": ["阿维达"]}) assert ret and 'title' in ret and ret['title'] == "阿维达" def test_forced_unicode_japanese_options(): ret = guessit("[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi", options={"expected_title": ["阿维达"]}) assert ret and 'title' in ret and ret['title'] == "阿维达" def test_ensure_custom_string_class(): class CustomStr(str): pass ret = guessit(CustomStr('some.title.1080p.mkv'), options={'advanced': True}) assert ret and 'screen_size' in ret and isinstance(ret['screen_size'].input_string, CustomStr) assert ret and 'title' in ret and isinstance(ret['title'].input_string, CustomStr) assert ret and 'container' in ret and isinstance(ret['container'].input_string, CustomStr) def test_properties(): props = properties() assert 'video_codec' in props.keys() def test_exception(): with pytest.raises(GuessitException) as excinfo: guessit(object()) assert "An internal error has occurred in guessit" in str(excinfo.value) assert "Guessit Exception Report" in str(excinfo.value) assert "Please report at https://github.com/guessit-io/guessit/issues" in str(excinfo.value) guessit-3.8.0/guessit/test/test_benchmark.py000066400000000000000000000024441453655371700212360ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=pointless-statement,missing-docstring,invalid-name,line-too-long import time import pytest from ..api import guessit def case1(): return guessit('Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') def case2(): return guessit('Movies/Fantastic Mr Fox/Fantastic.Mr.Fox.2009.DVDRip.{x264+LC-AAC.5.1}{Fr-Eng}{Sub.Fr-Eng}-™.[sharethefiles.com].mkv') def case3(): return guessit('Series/dexter/Dexter.5x02.Hello,.Bandit.ENG.-.sub.FR.HDTV.XviD-AlFleNi-TeaM.[tvu.org.ru].avi') def case4(): return guessit('Movies/The Doors (1991)/09.03.08.The.Doors.(1991).BDRip.720p.AC3.X264-HiS@SiLUHD-English.[sharethefiles.com].mkv') @pytest.mark.benchmark( group="Performance Tests", min_time=1, max_time=2, min_rounds=5, timer=time.time, disable_gc=True, warmup=False ) @pytest.mark.skipif(True, reason="Disabled") class TestBenchmark: def test_case1(self, benchmark): ret = benchmark(case1) assert ret def test_case2(self, benchmark): ret = benchmark(case2) assert ret def test_case3(self, benchmark): ret = benchmark(case3) assert ret def test_case4(self, benchmark): ret = benchmark(case4) assert ret guessit-3.8.0/guessit/test/test_main.py000066400000000000000000000046621453655371700202340ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=pointless-statement, missing-docstring, invalid-name import json import os import sys import pytest from _pytest.capture import CaptureFixture from ..__main__ import main __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) # Prevent output from spamming the console @pytest.fixture(scope="function", autouse=True) def no_stdout(monkeypatch): with open(os.devnull, "w") as f: # pylint:disable=unspecified-encoding monkeypatch.setattr(sys, "stdout", f) yield def test_main_no_args(): main([]) def test_main(): main(['Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv']) def test_main_unicode(): main(['[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi']) def test_main_forced_unicode(): main(['Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv']) def test_main_verbose(): main(['Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv', '--verbose']) def test_main_yaml(): main(['Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv', '--yaml']) def test_main_json(): main(['Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv', '--json']) def test_main_show_property(): main(['Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv', '-P', 'title']) def test_main_advanced(): main(['Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv', '-a']) def test_main_input(): main(['--input', os.path.join(__location__, 'test-input-file.txt')]) def test_main_properties(): main(['-p']) main(['-p', '--json']) main(['-p', '--yaml']) def test_main_values(): main(['-V']) main(['-V', '--json']) main(['-V', '--yaml']) def test_main_help(): with pytest.raises(SystemExit): main(['--help']) def test_main_version(): main(['--version']) def test_json_output_input_string(capsys: CaptureFixture): main(['--json', '--output-input-string', 'test.avi']) outerr = capsys.readouterr() data = json.loads(outerr.out) assert 'input_string' in data assert data['input_string'] == 'test.avi' def test_json_no_output_input_string(capsys: CaptureFixture): main(['--json', 'test.avi']) outerr = capsys.readouterr() data = json.loads(outerr.out) assert 'input_string' not in data guessit-3.8.0/guessit/test/test_options.py000066400000000000000000000140521453655371700207750ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=pointless-statement, missing-docstring, invalid-name, pointless-string-statement import os import pytest from ..options import get_options_file_locations, merge_options, load_config_file, ConfigurationException, \ load_config __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) def test_config_locations(): homedir = '/root' cwd = '/root/cwd' locations = get_options_file_locations(homedir, cwd, True) assert len(locations) == 9 assert '/root/.guessit/options.json' in locations assert '/root/.guessit/options.yml' in locations assert '/root/.guessit/options.yaml' in locations assert '/root/.config/guessit/options.json' in locations assert '/root/.config/guessit/options.yml' in locations assert '/root/.config/guessit/options.yaml' in locations assert '/root/cwd/guessit.options.json' in locations assert '/root/cwd/guessit.options.yml' in locations assert '/root/cwd/guessit.options.yaml' in locations def test_merge_configurations(): c1 = {'param1': True, 'param2': True, 'param3': False} c2 = {'param1': False, 'param2': True, 'param3': False} c3 = {'param1': False, 'param2': True, 'param3': False} merged = merge_options(c1, c2, c3) assert not merged['param1'] assert merged['param2'] assert not merged['param3'] merged = merge_options(c3, c2, c1) assert merged['param1'] assert merged['param2'] assert not merged['param3'] def test_merge_configurations_lists(): c1 = {'param1': [1], 'param2': True, 'param3': False} c2 = {'param1': [2], 'param2': True, 'param3': False} c3 = {'param1': [3], 'param2': True, 'param3': False} merged = merge_options(c1, c2, c3) assert merged['param1'] == [1, 2, 3] assert merged['param2'] assert not merged['param3'] merged = merge_options(c3, c2, c1) assert merged['param1'] == [3, 2, 1] assert merged['param2'] assert not merged['param3'] def test_merge_configurations_deep(): c1 = {'param1': [1], 'param2': {'d1': [1]}, 'param3': False} c2 = {'param1': [2], 'param2': {'d1': [2]}, 'param3': False} c3 = {'param1': [3], 'param2': {'d3': [3]}, 'param3': False} merged = merge_options(c1, c2, c3) assert merged['param1'] == [1, 2, 3] assert merged['param2']['d1'] == [1, 2] assert merged['param2']['d3'] == [3] assert 'd2' not in merged['param2'] assert not merged['param3'] merged = merge_options(c3, c2, c1) assert merged['param1'] == [3, 2, 1] assert merged['param2'] assert merged['param2']['d1'] == [2, 1] assert 'd2' not in merged['param2'] assert merged['param2']['d3'] == [3] assert not merged['param3'] def test_merge_configurations_pristine_all(): c1 = {'param1': [1], 'param2': True, 'param3': False} c2 = {'param1': [2], 'param2': True, 'param3': False, 'pristine': True} c3 = {'param1': [3], 'param2': True, 'param3': False} merged = merge_options(c1, c2, c3) assert merged['param1'] == [2, 3] assert merged['param2'] assert not merged['param3'] merged = merge_options(c3, c2, c1) assert merged['param1'] == [2, 1] assert merged['param2'] assert not merged['param3'] def test_merge_configurations_pristine_properties(): c1 = {'param1': [1], 'param2': False, 'param3': True} c2 = {'param1': [2], 'param2': True, 'param3': False, 'pristine': ['param2', 'param3']} c3 = {'param1': [3], 'param2': True, 'param3': False} merged = merge_options(c1, c2, c3) assert merged['param1'] == [1, 2, 3] assert merged['param2'] assert not merged['param3'] def test_merge_configurations_pristine_properties_deep(): c1 = {'param1': [1], 'param2': {'d1': False}, 'param3': True} c2 = {'param1': [2], 'param2': {'d1': True}, 'param3': False, 'pristine': ['param2', 'param3']} c3 = {'param1': [3], 'param2': {'d1': True}, 'param3': False} merged = merge_options(c1, c2, c3) assert merged['param1'] == [1, 2, 3] assert merged['param2'] assert not merged['param3'] def test_merge_configurations_pristine_properties2(): c1 = {'param1': [1], 'param2': False, 'param3': True} c2 = {'param1': [2], 'param2': True, 'param3': False, 'pristine': ['param1', 'param2', 'param3']} c3 = {'param1': [3], 'param2': True, 'param3': False} merged = merge_options(c1, c2, c3) assert merged['param1'] == [2, 3] assert merged['param2'] assert not merged['param3'] def test_load_config_file(): json_config = load_config_file(os.path.join(__location__, 'config', 'test.json')) yml_config = load_config_file(os.path.join(__location__, 'config', 'test.yml')) yaml_config = load_config_file(os.path.join(__location__, 'config', 'test.yaml')) assert json_config['expected_title'] == ['The 100', 'OSS 117'] assert yml_config['expected_title'] == ['The 100', 'OSS 117'] assert yaml_config['expected_title'] == ['The 100', 'OSS 117'] assert json_config['yaml'] is False assert yml_config['yaml'] is True assert yaml_config['yaml'] is True with pytest.raises(ConfigurationException) as excinfo: load_config_file(os.path.join(__location__, 'config', 'dummy.txt')) assert excinfo.match('Configuration file extension is not supported for ".*?dummy.txt" file\\.') def test_load_config(): config = load_config({'no_default_config': True, 'param1': 'test', 'config': [os.path.join(__location__, 'config', 'test.yml')]}) assert not config.get('param1') assert config.get('advanced_config') # advanced_config is still loaded from default assert config['expected_title'] == ['The 100', 'OSS 117'] assert config['yaml'] is True config = load_config({'no_default_config': True, 'param1': 'test'}) assert not config.get('param1') assert 'expected_title' not in config assert 'yaml' not in config config = load_config({'no_default_config': True, 'param1': 'test', 'config': ['false']}) assert not config.get('param1') assert 'expected_title' not in config assert 'yaml' not in config guessit-3.8.0/guessit/test/test_yml.py000066400000000000000000000242431453655371700201060ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=pointless-statement, missing-docstring, invalid-name import logging import os import babelfish import yaml # pylint:disable=wrong-import-order from rebulk.remodule import re from rebulk.utils import is_iterable from .. import guessit from ..options import parse_options from ..yamlutils import OrderedDictYAMLLoader logger = logging.getLogger(__name__) __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) class EntryResult: def __init__(self, string, negates=False): self.string = string self.negates = negates self.valid = [] self.missing = [] self.different = [] self.extra = [] self.others = [] @property def ok(self): if self.negates: return self.missing or self.different return not self.missing and not self.different and not self.extra and not self.others @property def warning(self): if self.negates: return False return not self.missing and not self.different and self.extra @property def error(self): if self.negates: return not self.missing and not self.different and not self.others return self.missing or self.different or self.others def __repr__(self): if self.ok: return self.string + ': OK!' if self.warning: return f'{"-" if self.negates else ""}{self.string}: ' \ f'WARNING! (valid={len(self.valid)}, extra={self.extra})' if self.error: return f'{"-" if self.negates else ""}{self.string}: ' \ f'ERROR! (valid={len(self.valid)}, extra={self.extra}, ' \ f'missing={self.missing}, different={self.different}, others={self.others})' return f'{"-" if self.negates else ""}{self.string}: ' \ f'UNKOWN! (valid={len(self.valid)}, extra={self.extra}, ' \ f'missing={self.missing}, different={self.different}, others={self.others})' @property def details(self): ret = [] if self.valid: ret.append('valid=' + str(len(self.valid))) for valid in self.valid: ret.append(' ' * 4 + str(valid)) if self.missing: ret.append('missing=' + str(len(self.missing))) for missing in self.missing: ret.append(' ' * 4 + str(missing)) if self.different: ret.append('different=' + str(len(self.different))) for different in self.different: ret.append(' ' * 4 + str(different)) if self.extra: ret.append('extra=' + str(len(self.extra))) for extra in self.extra: ret.append(' ' * 4 + str(extra)) if self.others: ret.append('others=' + str(len(self.others))) for other in self.others: ret.append(' ' * 4 + str(other)) return ret class Results(list): def assert_ok(self): errors = [entry for entry in self if entry.error] assert not errors def files_and_ids(predicate=None): files = [] ids = [] for (dirpath, _, filenames) in os.walk(__location__): if os.path.split(dirpath)[-1] == 'config': continue if dirpath == __location__: dirpath_rel = '' else: dirpath_rel = os.path.relpath(dirpath, __location__) for filename in filenames: name, ext = os.path.splitext(filename) filepath = os.path.join(dirpath_rel, filename) if ext in ['.yml', '.yaml'] and (not predicate or predicate(filepath)): files.append(filepath) ids.append(os.path.join(dirpath_rel, name)) return files, ids class TestYml: """ Run tests from yaml files. Multiple input strings having same expected results can be chained. Use $ marker to check inputs that should not match results. """ options_re = re.compile(r'^([ +-]+)(.*)') def _get_unique_id(self, collection, base_id): ret = base_id i = 2 while ret in collection: suffix = "-" + str(i) ret = base_id + suffix i += 1 return ret def pytest_generate_tests(self, metafunc): if 'yml_test_case' in metafunc.fixturenames: entries = [] entry_ids = [] entry_set = set() for filename, _ in zip(*files_and_ids()): with open(os.path.join(__location__, filename), 'r', encoding='utf-8') as infile: data = yaml.load(infile, OrderedDictYAMLLoader) last_expected = None for string, expected in reversed(list(data.items())): if expected is None: data[string] = last_expected else: last_expected = expected default = None try: default = data['__default__'] del data['__default__'] except KeyError: pass for string, expected in data.items(): TestYml.set_default(expected, default) string = TestYml.fix_encoding(string) entries.append((filename, string, expected)) unique_id = self._get_unique_id(entry_set, '[' + filename + '] ' + str(string)) entry_set.add(unique_id) entry_ids.append(unique_id) metafunc.parametrize('yml_test_case', entries, ids=entry_ids) @staticmethod def set_default(expected, default): if default: for k, v in default.items(): if k not in expected: expected[k] = v @classmethod def fix_encoding(cls, string): if not isinstance(string, str): string = str(string) return string def test_entry(self, yml_test_case): filename, string, expected = yml_test_case result = self.check_data(filename, string, expected) assert not result.error def check_data(self, filename, string, expected): entry = self.check(string, expected) if entry.ok: logger.debug('[%s] %s', filename, entry) elif entry.warning: logger.warning('[%s] %s', filename, entry) elif entry.error: logger.error('[%s] %s', filename, entry) for line in entry.details: logger.error('[%s] %s', filename, ' ' * 4 + line) return entry def check(self, string, expected): negates, global_, string = self.parse_token_options(string) options = expected.get('options') if options is None: options = {} if not isinstance(options, dict): options = parse_options(options) try: result = guessit(string, options) except Exception as exc: logger.error('[%s] Exception: %s', string, exc) raise exc entry = EntryResult(string, negates) if global_: self.check_global(string, result, entry) self.check_expected(result, expected, entry) return entry def parse_token_options(self, string): matches = self.options_re.search(string) negates = False global_ = False if matches: string = matches.group(2) for opt in matches.group(1): if '-' in opt: negates = True if '+' in opt: global_ = True return negates, global_, string def check_global(self, string, result, entry): global_span = [] for result_matches in result.matches.values(): for result_match in result_matches: if not global_span: global_span = list(result_match.span) else: if global_span[0] > result_match.span[0]: global_span[0] = result_match.span[0] if global_span[1] < result_match.span[1]: global_span[1] = result_match.span[1] if global_span and global_span[1] - global_span[0] < len(string): entry.others.append("Match is not global") def is_same(self, value, expected): values = set(value) if is_iterable(value) else set((value,)) expecteds = set(expected) if is_iterable(expected) else set((expected,)) if len(values) != len(expecteds): return False if isinstance(next(iter(values)), babelfish.Language): # pylint: disable=no-member expecteds = {babelfish.Language.fromguessit(expected) for expected in expecteds} elif isinstance(next(iter(values)), babelfish.Country): # pylint: disable=no-member expecteds = {babelfish.Country.fromguessit(expected) for expected in expecteds} return values == expecteds def check_expected(self, result, expected, entry): if expected: for expected_key, expected_value in expected.items(): if expected_key and expected_key != 'options' and expected_value is not None: negates_key, _, result_key = self.parse_token_options(expected_key) if result_key in result.keys(): if not self.is_same(result[result_key], expected_value): if negates_key: entry.valid.append((expected_key, expected_value)) else: entry.different.append((expected_key, expected_value, result[result_key])) else: if negates_key: entry.different.append((expected_key, expected_value, result[result_key])) else: entry.valid.append((expected_key, expected_value)) elif not negates_key: entry.missing.append((expected_key, expected_value)) for result_key, result_value in result.items(): if result_key not in expected.keys(): entry.extra.append((result_key, result_value)) guessit-3.8.0/guessit/test/various.yml000066400000000000000000000617251453655371700201150ustar00rootroot00000000000000? Movies/Fear and Loathing in Las Vegas (1998)/Fear.and.Loathing.in.Las.Vegas.720p.HDDVD.DTS.x264-ESiR.mkv : type: movie title: Fear and Loathing in Las Vegas year: 1998 screen_size: 720p source: HD-DVD audio_codec: DTS video_codec: H.264 release_group: ESiR ? Series/Duckman/Duckman - 101 (01) - 20021107 - I, Duckman.avi : type: episode title: Duckman season: 1 episode: 1 episode_title: I, Duckman date: 2002-11-07 ? Series/Neverwhere/Neverwhere.05.Down.Street.[tvu.org.ru].avi : type: episode title: Neverwhere episode: 5 episode_title: Down Street website: tvu.org.ru ? Neverwhere.05.Down.Street.[tvu.org.ru].avi : type: episode title: Neverwhere episode: 5 episode_title: Down Street website: tvu.org.ru ? Series/Breaking Bad/Minisodes/Breaking.Bad.(Minisodes).01.Good.Cop.Bad.Cop.WEBRip.XviD.avi : type: episode title: Breaking Bad episode_format: Minisode episode: 1 episode_title: Good Cop Bad Cop source: Web other: Rip video_codec: Xvid ? Series/Kaamelott/Kaamelott - Livre V - Ep 23 - Le Forfait.avi : type: episode title: Kaamelott episode: 23 episode_title: Le Forfait ? Movies/The Doors (1991)/09.03.08.The.Doors.(1991).BDRip.720p.AC3.X264-HiS@SiLUHD-English.[sharethefiles.com].mkv : type: movie title: The Doors year: 1991 date: 2008-03-09 source: Blu-ray screen_size: 720p audio_codec: Dolby Digital video_codec: H.264 release_group: HiS@SiLUHD language: english website: sharethefiles.com ? Movies/M.A.S.H. (1970)/MASH.(1970).[Divx.5.02][Dual-Subtitulos][DVDRip].ogm : type: movie title: MASH year: 1970 video_codec: DivX source: DVD other: [Dual Audio, Rip] ? the.mentalist.501.hdtv-lol.mp4 : type: episode title: the mentalist season: 5 episode: 1 source: HDTV release_group: lol ? the.simpsons.2401.hdtv-lol.mp4 : type: episode title: the simpsons season: 24 episode: 1 source: HDTV release_group: lol ? Homeland.S02E01.HDTV.x264-EVOLVE.mp4 : type: episode title: Homeland season: 2 episode: 1 source: HDTV video_codec: H.264 release_group: EVOLVE ? /media/Band_of_Brothers-e01-Currahee.mkv : type: episode title: Band of Brothers episode: 1 episode_title: Currahee ? /media/Band_of_Brothers-x02-We_Stand_Alone_Together.mkv : type: episode title: Band of Brothers bonus: 2 bonus_title: We Stand Alone Together ? /movies/James_Bond-f21-Casino_Royale-x02-Stunts.mkv : type: movie title: Casino Royale film_title: James Bond film: 21 bonus: 2 bonus_title: Stunts ? /TV Shows/new.girl.117.hdtv-lol.mp4 : type: episode title: new girl season: 1 episode: 17 source: HDTV release_group: lol ? The.Office.(US).1x03.Health.Care.HDTV.XviD-LOL.avi : type: episode title: The Office country: US season: 1 episode: 3 episode_title: Health Care source: HDTV video_codec: Xvid release_group: LOL ? The_Insider-(1999)-x02-60_Minutes_Interview-1996.mp4 : type: movie title: The Insider year: 1999 bonus: 2 bonus_title: 60 Minutes Interview-1996 ? OSS_117--Cairo,_Nest_of_Spies.mkv : type: movie title: OSS 117 alternative_title: Cairo, Nest of Spies ? Rush.._Beyond_The_Lighted_Stage-x09-Between_Sun_and_Moon-2002_Hartford.mkv : type: movie title: Rush Beyond The Lighted Stage bonus: 9 bonus_title: Between Sun and Moon year: 2002 ? House.Hunters.International.S56E06.720p.hdtv.x264.mp4 : type: episode title: House Hunters International season: 56 episode: 6 screen_size: 720p source: HDTV video_codec: H.264 ? White.House.Down.2013.1080p.BluRay.DTS-HD.MA.5.1.x264-PublicHD.mkv : type: movie title: White House Down year: 2013 screen_size: 1080p source: Blu-ray audio_codec: DTS-HD audio_profile: Master Audio video_codec: H.264 release_group: PublicHD audio_channels: "5.1" ? White.House.Down.2013.1080p.BluRay.DTSHD.MA.5.1.x264-PublicHD.mkv : type: movie title: White House Down year: 2013 screen_size: 1080p source: Blu-ray audio_codec: DTS-HD audio_profile: Master Audio video_codec: H.264 release_group: PublicHD audio_channels: "5.1" ? Hostages.S01E01.Pilot.for.Air.720p.WEB-DL.DD5.1.H.264-NTb.nfo : type: episode title: Hostages episode_title: Pilot for Air season: 1 episode: 1 screen_size: 720p source: Web audio_channels: "5.1" video_codec: H.264 audio_codec: Dolby Digital release_group: NTb ? Despicable.Me.2.2013.1080p.BluRay.x264-VeDeTT.nfo : type: movie title: Despicable Me 2 year: 2013 screen_size: 1080p source: Blu-ray video_codec: H.264 release_group: VeDeTT ? Le Cinquieme Commando 1971 SUBFORCED FRENCH DVDRiP XViD AC3 Bandix.mkv : type: movie audio_codec: Dolby Digital source: DVD other: Rip release_group: Bandix subtitle_language: French title: Le Cinquieme Commando video_codec: Xvid year: 1971 ? Le Seigneur des Anneaux - La Communauté de l'Anneau - Version Longue - BDRip.mkv : type: movie title: Le Seigneur des Anneaux source: Blu-ray other: Rip ? La petite bande (Michel Deville - 1983) VF PAL MP4 x264 AAC.mkv : type: movie audio_codec: AAC language: French title: La petite bande video_codec: H.264 year: 1983 other: PAL ? Retour de Flammes (Gregor Schnitzler 2003) FULL DVD.iso : type: movie source: DVD title: Retour de Flammes type: movie year: 2003 ? A.Common.Title.Special.2014.avi : type: movie year: 2014 title: A Common Title Special ? A.Common.Title.2014.Special.avi : type: episode year: 2014 title: A Common Title episode_title: Special episode_details: Special ? A.Common.Title.2014.Special.Edition.avi : type: movie year: 2014 title: A Common Title edition: Special ? Downton.Abbey.2013.Christmas.Special.HDTV.x264-FoV.mp4 : type: episode year: 2013 title: Downton Abbey episode_title: Christmas Special video_codec: H.264 release_group: FoV source: HDTV episode_details: Special ? Doctor_Who_2013_Christmas_Special.The_Time_of_The_Doctor.HD : type: episode title: Doctor Who other: HD episode_details: Special episode_title: Christmas Special The Time of The Doctor year: 2013 ? Doctor Who 2005 50th Anniversary Special The Day of the Doctor 3.avi : type: episode title: Doctor Who episode_details: Special episode_title: 50th Anniversary Special The Day of the Doctor 3 year: 2005 ? Robot Chicken S06-Born Again Virgin Christmas Special HDTV x264.avi : type: episode title: Robot Chicken source: HDTV season: 6 episode_title: Born Again Virgin Christmas Special video_codec: H.264 episode_details: Special ? Wicked.Tuna.S03E00.Head.To.Tail.Special.HDTV.x264-YesTV : type: episode title: Wicked Tuna episode_title: Head To Tail Special release_group: YesTV season: 3 episode: 0 video_codec: H.264 source: HDTV episode_details: Special ? The.Voice.UK.S03E12.HDTV.x264-C4TV : episode: 12 video_codec: H.264 source: HDTV title: The Voice release_group: C4TV season: 3 country: United Kingdom type: episode ? /tmp/star.trek.9/star.trek.9.mkv : type: movie title: star trek 9 ? star.trek.9.mkv : type: movie title: star trek 9 ? FlexGet.S01E02.TheName.HDTV.xvid : episode: 2 source: HDTV season: 1 title: FlexGet episode_title: TheName type: episode video_codec: Xvid ? FlexGet.S01E02.TheName.HDTV.xvid : episode: 2 source: HDTV season: 1 title: FlexGet episode_title: TheName type: episode video_codec: Xvid ? some.series.S03E14.Title.Here.720p : episode: 14 screen_size: 720p season: 3 title: some series episode_title: Title Here type: episode ? '[the.group] Some.Series.S03E15.Title.Two.720p' : episode: 15 release_group: the.group screen_size: 720p season: 3 title: Some Series episode_title: Title Two type: episode ? 'HD 720p: Some series.S03E16.Title.Three' : episode: 16 other: HD screen_size: 720p season: 3 title: Some series episode_title: Title Three type: episode ? Something.Season.2.1of4.Ep.Title.HDTV.torrent : episode_count: 4 episode: 1 source: HDTV season: 2 title: Something episode_title: Title type: episode container: torrent ? Show-A (US) - Episode Title S02E09 hdtv : country: US episode: 9 source: HDTV season: 2 title: Show-A type: episode ? Jack's.Show.S03E01.blah.1080p : episode: 1 screen_size: 1080p season: 3 title: Jack's Show episode_title: blah type: episode ? FlexGet.epic : title: FlexGet epic type: movie ? FlexGet.Apt.1 : title: FlexGet Apt 1 type: movie ? FlexGet.aptitude : title: FlexGet aptitude type: movie ? FlexGet.Step1 : title: FlexGet Step1 type: movie ? Movies/El Bosque Animado (1987)/El.Bosque.Animado.[Jose.Luis.Cuerda.1987].[Xvid-Dvdrip-720 * 432].avi : source: DVD other: Rip screen_size: 720x432 title: El Bosque Animado video_codec: Xvid year: 1987 type: movie ? Movies/El Bosque Animado (1987)/El.Bosque.Animado.[Jose.Luis.Cuerda.1987].[Xvid-Dvdrip-720x432].avi : source: DVD other: Rip screen_size: 720x432 title: El Bosque Animado video_codec: Xvid year: 1987 type: movie ? 2009.shoot.fruit.chan.multi.dvd9.pal : source: DVD language: mul other: PAL title: shoot fruit chan type: movie year: 2009 ? 2009.shoot.fruit.chan.multi.dvd5.pal : source: DVD language: mul other: PAL title: shoot fruit chan type: movie year: 2009 ? The.Flash.2014.S01E01.PREAIR.WEBRip.XviD-EVO.avi : episode: 1 source: Web other: [Preair, Rip] release_group: EVO season: 1 title: The Flash type: episode video_codec: Xvid year: 2014 ? Ice.Lake.Rebels.S01E06.Ice.Lake.Games.720p.HDTV.x264-DHD : episode: 6 source: HDTV release_group: DHD screen_size: 720p season: 1 title: Ice Lake Rebels episode_title: Ice Lake Games type: episode video_codec: H.264 ? The League - S06E10 - Epi Sexy.mkv : episode: 10 season: 6 title: The League episode_title: Epi Sexy type: episode ? Stay (2005) [1080p]/Stay.2005.1080p.BluRay.x264.YIFY.mp4 : source: Blu-ray release_group: YIFY screen_size: 1080p title: Stay type: movie video_codec: H.264 year: 2005 ? /media/live/A/Anger.Management.S02E82.720p.HDTV.X264-DIMENSION.mkv : source: HDTV release_group: DIMENSION screen_size: 720p title: Anger Management type: episode season: 2 episode: 82 video_codec: H.264 ? "[Figmentos] Monster 34 - At the End of Darkness [781219F1].mkv" : type: episode release_group: Figmentos title: Monster episode: 34 episode_title: At the End of Darkness crc32: 781219F1 ? Game.of.Thrones.S05E07.720p.HDTV-KILLERS.mkv : type: episode episode: 7 source: HDTV release_group: KILLERS screen_size: 720p season: 5 title: Game of Thrones ? Game.of.Thrones.S05E07.HDTV.720p-KILLERS.mkv : type: episode episode: 7 source: HDTV release_group: KILLERS screen_size: 720p season: 5 title: Game of Thrones ? Parks and Recreation - [04x12] - Ad Campaign.avi : type: episode title: Parks and Recreation season: 4 episode: 12 episode_title: Ad Campaign ? Star Trek Into Darkness (2013)/star.trek.into.darkness.2013.720p.web-dl.h264-publichd.mkv : type: movie title: Star Trek Into Darkness year: 2013 screen_size: 720p source: Web video_codec: H.264 release_group: publichd ? /var/medias/series/The Originals/Season 02/The.Originals.S02E15.720p.HDTV.X264-DIMENSION.mkv : type: episode title: The Originals season: 2 episode: 15 screen_size: 720p source: HDTV video_codec: H.264 release_group: DIMENSION ? Test.S01E01E07-FooBar-Group.avi : container: avi episode: - 1 - 7 episode_title: FooBar-Group # Make sure it doesn't conflict with uuid season: 1 title: Test type: episode ? TEST.S01E02.2160p.NF.WEBRip.x264.DD5.1-ABC : audio_channels: '5.1' audio_codec: Dolby Digital episode: 2 source: Web other: Rip release_group: ABC screen_size: 2160p season: 1 streaming_service: Netflix title: TEST type: episode video_codec: H.264 ? TEST.2015.12.30.720p.WEBRip.h264-ABC : date: 2015-12-30 source: Web other: Rip release_group: ABC screen_size: 720p title: TEST type: episode video_codec: H.264 ? TEST.S01E10.24.1080p.NF.WEBRip.AAC2.0.x264-ABC : audio_channels: '2.0' audio_codec: AAC episode: 10 episode_title: '24' source: Web other: Rip release_group: ABC screen_size: 1080p season: 1 streaming_service: Netflix title: TEST type: episode video_codec: H.264 ? TEST.S01E10.24.1080p.NF.WEBRip.AAC2.0.x264-ABC : audio_channels: '2.0' audio_codec: AAC episode: 10 episode_title: '24' source: Web other: Rip release_group: ABC screen_size: 1080p season: 1 streaming_service: Netflix title: TEST type: episode video_codec: H.264 ? TEST.S01E10.24.1080p.NF.WEBRip.AAC.2.0.x264-ABC : audio_channels: '2.0' audio_codec: AAC episode: 10 episode_title: '24' source: Web other: Rip release_group: ABC screen_size: 1080p season: 1 streaming_service: Netflix title: TEST type: episode video_codec: H.264 ? TEST.S05E02.720p.iP.WEBRip.AAC2.0.H264-ABC : audio_channels: '2.0' audio_codec: AAC episode: 2 source: Web other: Rip release_group: ABC screen_size: 720p season: 5 title: TEST type: episode video_codec: H.264 ? TEST.S03E07.720p.WEBRip.AAC2.0.x264-ABC : audio_channels: '2.0' audio_codec: AAC episode: 7 source: Web other: Rip release_group: ABC screen_size: 720p season: 3 title: TEST type: episode video_codec: H.264 ? TEST.S15E15.24.1080p.FREE.WEBRip.AAC2.0.x264-ABC : audio_channels: '2.0' audio_codec: AAC episode: 15 episode_title: '24' source: Web other: Rip release_group: ABC screen_size: 1080p season: 15 title: TEST type: episode video_codec: H.264 ? TEST.S11E11.24.720p.ETV.WEBRip.AAC2.0.x264-ABC : audio_channels: '2.0' audio_codec: AAC episode: 11 episode_title: '24' source: Web other: Rip release_group: ABC screen_size: 720p season: 11 title: TEST type: episode video_codec: H.264 ? TEST.2015.1080p.HC.WEBRip.x264.AAC2.0-ABC : audio_channels: '2.0' audio_codec: AAC source: Web other: Rip release_group: ABC screen_size: 1080p title: TEST type: movie video_codec: H.264 year: 2015 ? TEST.2015.1080p.3D.BluRay.Half-SBS.x264.DTS-HD.MA.7.1-ABC : audio_channels: '7.1' audio_codec: DTS-HD audio_profile: Master Audio source: Blu-ray other: 3D release_group: ABC screen_size: 1080p title: TEST type: movie video_codec: H.264 year: 2015 ? TEST.2015.1080p.3D.BluRay.Half-OU.x264.DTS-HD.MA.7.1-ABC : audio_channels: '7.1' audio_codec: DTS-HD audio_profile: Master Audio source: Blu-ray other: 3D release_group: ABC screen_size: 1080p title: TEST type: movie video_codec: H.264 year: 2015 ? TEST.2015.1080p.3D.BluRay.Half-OU.x264.DTS-HD.MA.TrueHD.7.1.Atmos-ABC : audio_channels: '7.1' audio_codec: - DTS-HD - Dolby TrueHD - Dolby Atmos audio_profile: Master Audio source: Blu-ray other: 3D release_group: ABC screen_size: 1080p title: TEST type: movie video_codec: H.264 year: 2015 ? TEST.2015.1080p.3D.BluRay.Half-SBS.x264.DTS-HD.MA.TrueHD.7.1.Atmos-ABC : audio_channels: '7.1' audio_codec: - DTS-HD - Dolby TrueHD - Dolby Atmos audio_profile: Master Audio source: Blu-ray other: 3D release_group: ABC screen_size: 1080p title: TEST type: movie video_codec: H.264 year: 2015 ? TEST.2015.1080p.BluRay.REMUX.AVC.DTS-HD.MA.TrueHD.7.1.Atmos-ABC : audio_channels: '7.1' audio_codec: - DTS-HD - Dolby TrueHD - Dolby Atmos audio_profile: Master Audio source: Blu-ray other: Remux release_group: ABC screen_size: 1080p title: TEST type: movie year: 2015 ? Gangs of New York 2002 REMASTERED 1080p BluRay x264-AVCHD : source: Blu-ray edition: Remastered screen_size: 1080p title: Gangs of New York type: movie video_codec: H.264 video_profile: Advanced Video Codec High Definition year: 2002 ? Peep.Show.S06E02.DVDrip.x264-faks86.mkv : container: mkv episode: 2 source: DVD other: Rip release_group: faks86 season: 6 title: Peep Show type: episode video_codec: H.264 # Episode title is indeed 'October 8, 2014' # https://thetvdb.com/?tab=episode&seriesid=82483&seasonid=569935&id=4997362&lid=7 ? The Soup - 11x41 - October 8, 2014.mp4 : container: mp4 episode: 41 episode_title: October 8, 2014 season: 11 title: The Soup type: episode ? Red.Rock.S02E59.WEB-DLx264-JIVE : episode: 59 season: 2 source: Web release_group: JIVE title: Red Rock type: episode video_codec: H.264 ? Pawn.Stars.S12E31.Deals.On.Wheels.PDTVx264-JIVE : episode: 31 episode_title: Deals On Wheels season: 12 source: Digital TV release_group: JIVE title: Pawn Stars type: episode video_codec: H.264 ? Duck.Dynasty.S09E09.Van.He-llsing.HDTVx264-JIVE : episode: 9 episode_title: Van He-llsing season: 9 source: HDTV release_group: JIVE title: Duck Dynasty type: episode video_codec: H.264 ? ATKExotics.16.01.24.Ava.Alba.Watersports.XXX.1080p.MP4-KTR : title: ATKExotics episode_title: Ava Alba Watersports other: XXX screen_size: 1080p container: mp4 release_group: KTR type: episode ? PutaLocura.15.12.22.Spanish.Luzzy.XXX.720p.MP4-oRo : title: PutaLocura episode_title: Spanish Luzzy other: XXX screen_size: 720p container: mp4 release_group: oRo type: episode ? French Maid Services - Lola At Your Service WEB-DL SPLIT SCENES MP4-RARBG : title: French Maid Services alternative_title: Lola At Your Service source: Web container: mp4 release_group: RARBG type: movie ? French Maid Services - Lola At Your Service - Marc Dorcel WEB-DL SPLIT SCENES MP4-RARBG : title: French Maid Services alternative_title: [Lola At Your Service, Marc Dorcel] source: Web container: mp4 release_group: RARBG type: movie ? PlayboyPlus.com_16.01.23.Eleni.Corfiate.Playboy.Romania.XXX.iMAGESET-OHRLY : episode_title: Eleni Corfiate Playboy Romania other: XXX type: episode ? TeenPornoPass - Anna - Beautiful Ass Deep Penetrated 720p mp4 : title: TeenPornoPass alternative_title: - Anna - Beautiful Ass Deep Penetrated screen_size: 720p container: mp4 type: movie ? SexInJeans.Gina.Gerson.Super.Nasty.Asshole.Pounding.With.Gina.In.Jeans.A.Devil.In.Denim.The.Finest.Ass.Fuck.Frolicking.mp4 : title: SexInJeans Gina Gerson Super Nasty Asshole Pounding With Gina In Jeans A Devil In Denim The Finest Ass Fuck Frolicking container: mp4 type: movie ? TNA Impact Wrestling HDTV 2017-06-22 720p H264 AVCHD-SC-SDH : title: TNA Impact Wrestling source: HDTV date: 2017-06-22 screen_size: 720p video_codec: H.264 video_profile: - Advanced Video Codec High Definition - Scalable Video Coding release_group: SDH type: episode ? 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 Series 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 ? This.is.Us : options: --no-default-config title: This is Us type: movie ? This.is.Us : options: --excludes country title: This is Us type: movie ? MotoGP.2016x03.USA.Race.BTSportHD.1080p25 : title: MotoGP season: 2016 year: 2016 episode: 3 screen_size: 1080p frame_rate: 25fps type: episode ? BBC.Earth.South.Pacific.2010.D2.1080p.24p.BD25.DTS-HD : title: BBC Earth South Pacific year: 2010 screen_size: 1080p frame_rate: 24fps source: Blu-ray audio_codec: DTS-HD type: movie ? Mr Robot - S03E01 - eps3 0 power-saver-mode h (1080p AMZN WEB-DL x265 HEVC 10bit EAC3 6.0 RCVR).mkv : title: Mr Robot season: 3 episode: 1 episode_title: eps3 0 power-saver-mode h screen_size: 1080p streaming_service: Amazon Prime source: Web video_codec: H.265 video_profile: High Efficiency Video Coding color_depth: 10-bit audio_codec: Dolby Digital Plus audio_channels: '5.1' release_group: RCVR container: mkv type: episode ? Panorama.15-05-2018.Web-DL.540p.H264.AAC.Subs.mp4 : title: Panorama date: 2018-05-15 source: Web screen_size: 540p video_codec: H.264 audio_codec: AAC subtitle_language: und container: mp4 type: episode ? Shaolin 2011.720p.BluRay.x264-x0r.mkv : title: Shaolin year: 2011 screen_size: 720p source: Blu-ray video_codec: H.264 release_group: x0r container: mkv type: movie ? '[ Engineering Catastrophes S02E10 1080p AMZN WEB-DL DD+ 2.0 x264-TrollHD ]' : title: Engineering Catastrophes season: 2 episode: 10 screen_size: 1080p streaming_service: Amazon Prime source: Web audio_codec: Dolby Digital Plus audio_channels: '2.0' video_codec: H.264 release_group: TrollHD type: episode ? A Very Harold & Kumar 3D Christmas (2011).mkv : title: A Very Harold & Kumar 3D Christmas year: 2011 container: mkv type: movie ? Cleveland.Hustles.S01E03.Downward.Dogs.and.Proper.Pigs.720p.HDTV.x264-W4F : title: Cleveland Hustles season: 1 episode: 3 episode_title: Downward Dogs and Proper Pigs screen_size: 720p source: HDTV video_codec: H.264 release_group: W4F type: episode ? Pawn.Stars.S12E20.The.Pawn.Awakens.REAL.READ.NFO.720p.HDTV.x264-DHD : title: Pawn Stars season: 12 episode: 20 episode_title: The Pawn Awakens other: - Proper - Read NFO proper_count: 2 screen_size: 720p source: HDTV video_codec: H.264 release_group: DHD type: episode ? Pawn.Stars.S12E22.Racing.Revolution.REAL.720p.HDTV.x264-DHD : title: Pawn Stars season: 12 episode: 22 episode_title: Racing Revolution other: Proper proper_count: 2 screen_size: 720p source: HDTV video_codec: H.264 release_group: DHD type: episode ? Luksusfellen.S18E02.REAL.NORWEGiAN.720p.WEB.h264-NORPiLT : title: Luksusfellen season: 18 episode: 2 other: Proper proper_count: 2 language: Norwegian screen_size: 720p source: Web video_codec: H.264 release_group: NORPiLT type: episode ? The.Exorcist.S02E07.REAL.FRENCH.720p.HDTV.x264-SH0W : title: The Exorcist season: 2 episode: 7 other: Proper proper_count: 2 language: fr screen_size: 720p source: HDTV video_codec: H.264 release_group: SH0W type: episode ? Outrageous.Acts.of.Science.S05E02.Is.This.for.Real.720p.HDTV.x264-DHD : title: Outrageous Acts of Science season: 5 episode: 2 # corner case # episode_title: Is This for Real screen_size: 720p source: HDTV video_codec: H.264 release_group: DHD type: episode ? How.the.Universe.Works.S06E08.Strange.Lives.of.Dwarf.Planets.REAL.720p.WEB.x264-DHD : title: How the Universe Works season: 6 episode: 8 episode_title: Strange Lives of Dwarf Planets other: Proper proper_count: 2 screen_size: 720p source: Web video_codec: H.264 release_group: DHD type: episode ? Vampirina.S01E16.REAL.HDTV.x264-W4F : title: Vampirina season: 1 episode: 16 other: Proper proper_count: 2 source: HDTV video_codec: H.264 release_group: W4F type: episode ? Test.S01E16.Some Real Episode Title.HDTV.x264-W4F : title: Test season: 1 episode: 16 episode_title: Some Real Episode Title source: HDTV video_codec: H.264 release_group: W4F type: episode ? NOS4A2.S01E01.The.Shorter.Way.REPACK.720p.AMZN.WEB-DL.DDP5.1.H.264-NTG.mkv : title: NOS4A2 season: 1 episode: 1 episode_title: The Shorter Way other: Proper proper_count: 1 screen_size: 720p streaming_service: Amazon Prime source: Web audio_codec: Dolby Digital Plus audio_channels: '5.1' video_codec: H.264 release_group: NTG container: mkv type: episode ? Star Trek DS9 Ep 2x03 The Siege (Part III) : title: Star Trek DS9 season: 2 episode: 3 episode_title: The Siege part: 3 type: episode ? The.Red.Line.S01E01 : title: The Red Line season: 1 episode: 1 type: episode ? Show.S01E01.WEB.x264-METCON.mkv : title: Show season: 1 episode: 1 source: Web video_codec: H.264 release_group: METCON container: mkv type: episode ? Show.S01E01.WEB.x264-TCMEON.mkv : title: Show season: 1 episode: 1 source: Web video_codec: H.264 release_group: TCMEON container: mkv type: episode ? Show.S01E01.WEB.x264-MEONTC.mkv : title: Show season: 1 episode: 1 source: Web video_codec: H.264 release_group: MEONTC container: mkv type: episode ? '[TorrentCouch.com].Westworld.S02.Complete.720p.WEB-DL.x264.[MP4].[5.3GB].[Season.2.Full]/[TorrentCouch.com].Westworld.S02E03.720p.WEB-DL.x264.mp4' : website: TorrentCouch.com title: Westworld season: 2 other: Complete screen_size: 720p source: Web video_codec: H.264 container: mp4 size: 5.3GB episode: 3 type: episode ? Vita.&.Virginia.2018.720p.H.264.YTS.LT.mp4 : title: Vita & Virginia year: 2018 screen_size: 720p video_codec: H.264 release_group: YTS.LT container: mp4 type: movieguessit-3.8.0/guessit/yamlutils.py000066400000000000000000000050561453655371700173130ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Options """ from collections import OrderedDict import babelfish import yaml # pylint:disable=wrong-import-order from .rules.common.quantity import BitRate, FrameRate, Size class OrderedDictYAMLLoader(yaml.SafeLoader): """ A YAML loader that loads mappings into ordered dictionaries. From https://gist.github.com/enaeseth/844388 """ def __init__(self, *args, **kwargs): yaml.SafeLoader.__init__(self, *args, **kwargs) self.add_constructor('tag:yaml.org,2002:map', type(self).construct_yaml_map) self.add_constructor('tag:yaml.org,2002:omap', type(self).construct_yaml_map) def construct_yaml_map(self, node): data = OrderedDict() yield data value = self.construct_mapping(node) data.update(value) def construct_mapping(self, node, deep=False): if isinstance(node, yaml.MappingNode): self.flatten_mapping(node) else: # pragma: no cover raise yaml.constructor.ConstructorError(None, None, f'expected a mapping node, but found {node.id}', node.start_mark) mapping = OrderedDict() for key_node, value_node in node.value: key = self.construct_object(key_node, deep=deep) try: hash(key) except TypeError as exc: # pragma: no cover raise yaml.constructor.ConstructorError('while constructing a mapping', node.start_mark, f'found unacceptable key ({exc})' , key_node.start_mark) value = self.construct_object(value_node, deep=deep) mapping[key] = value return mapping class CustomDumper(yaml.SafeDumper): """ Custom YAML Dumper. """ pass # pylint:disable=unnecessary-pass def default_representer(dumper, data): """Default representer""" return dumper.represent_str(str(data)) CustomDumper.add_representer(babelfish.Language, default_representer) CustomDumper.add_representer(babelfish.Country, default_representer) CustomDumper.add_representer(BitRate, default_representer) CustomDumper.add_representer(FrameRate, default_representer) CustomDumper.add_representer(Size, default_representer) def ordered_dict_representer(dumper, data): """OrderedDict representer""" return dumper.represent_mapping('tag:yaml.org,2002:map', data.items()) CustomDumper.add_representer(OrderedDict, ordered_dict_representer) guessit-3.8.0/mkdocs.yml000066400000000000000000000013501453655371700152270ustar00rootroot00000000000000site_name: GuessIt site_url: https://guessit-io.github.io/guessit site_description: GuessIt is a python library that extracts as much information as possible from a video filename. site_author: Rémi Alvergnat repo_url: https://github.com/guessit-io/guessit edit_uri: https://github.com/guessit-io/guessit/blob/develop/docs theme: language: 'en' name: 'material' markdown_extensions: - admonition - codehilite - toc: permalink: true - pymdownx.details - pymdownx.superfences nav: - Home: index.md - Properties: properties.md - Sources: sources.md - Configuration: configuration.md - Migration (2.x to 3.x): migration2to3.md - Migration (1.x to 2.x): migration.md plugins: - search guessit-3.8.0/pylintrc000066400000000000000000000502011453655371700150120ustar00rootroot00000000000000[MAIN] # Analyse import fallback blocks. This can be used to support both Python 2 and # 3 compatible code, which means that the block might have code that exists # only in one or another interpreter, leading to false positives when analysed. analyse-fallback-blocks=no # Load and enable all available extensions. Use --list-extensions to see a list # all available extensions. #enable-all-extensions= # In error mode, messages with a category besides ERROR or FATAL are # suppressed, and no reports are done by default. Error mode is compatible with # disabling specific errors. #errors-only= # Always return a 0 (non-error) status code, even if lint errors are found. # This is primarily useful in continuous integration scripts. #exit-zero= # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code. extension-pkg-allow-list= # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code. (This is an alternative name to extension-pkg-allow-list # for backward compatibility.) extension-pkg-whitelist= # Return non-zero exit code if any of these messages/categories are detected, # even if score is above --fail-under value. Syntax same as enable. Messages # specified are enabled, while categories only check already-enabled messages. fail-on= # Specify a score threshold under which the program will exit with error. fail-under=10 # Interpret the stdin as a python script, whose filename needs to be passed as # the module_or_package argument. #from-stdin= # Files or directories to be skipped. They should be base names, not paths. ignore=CVS # Add files or directories matching the regular expressions patterns to the # ignore-list. The regex matches against paths and can be in Posix or Windows # format. Because '\' represents the directory delimiter on Windows systems, it # can't be used as an escape character. ignore-paths= # Files or directories matching the regular expression patterns are skipped. # The regex matches against base names, not paths. The default value ignores # Emacs file locks ignore-patterns=^\.# # List of module names for which member attributes should not be checked # (useful for modules/projects where namespaces are manipulated during runtime # and thus existing member attributes cannot be deduced by static analysis). It # supports qualified module names, as well as Unix pattern matching. ignored-modules= # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). #init-hook= # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the # number of processors available to use, and will cap the count on Windows to # avoid hangs. jobs=1 # Control the amount of potential inferred values when inferring a single # object. This can help the performance when dealing with large functions or # complex, nested conditions. limit-inference-results=100 # List of plugins (as comma separated values of python module names) to load, # usually to register additional checkers. load-plugins= # Pickle collected data for later comparisons. persistent=yes # Minimum Python version to use for version dependent checks. Will default to # the version used to run pylint. py-version=3.9 # Discover python modules and packages in the file system subtree. recursive=no # When enabled, pylint would attempt to guess common misconfiguration and emit # user-friendly hints instead of false-positive error messages. suggestion-mode=yes # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. unsafe-load-any-extension=no # In verbose mode, extra non-checker-related info will be displayed. #verbose= [BASIC] # Naming style matching correct argument names. argument-naming-style=snake_case # Regular expression matching correct argument names. Overrides argument- # naming-style. If left empty, argument names will be checked with the set # naming style. #argument-rgx= # Naming style matching correct attribute names. attr-naming-style=snake_case # Regular expression matching correct attribute names. Overrides attr-naming- # style. If left empty, attribute names will be checked with the set naming # style. #attr-rgx= # Bad variable names which should always be refused, separated by a comma. bad-names=foo, bar, baz, toto, tutu, tata # Bad variable names regexes, separated by a comma. If names match any regex, # they will always be refused bad-names-rgxs= # Naming style matching correct class attribute names. class-attribute-naming-style=any # Regular expression matching correct class attribute names. Overrides class- # attribute-naming-style. If left empty, class attribute names will be checked # with the set naming style. #class-attribute-rgx= # Naming style matching correct class constant names. class-const-naming-style=snake_case # Regular expression matching correct class constant names. Overrides class- # const-naming-style. If left empty, class constant names will be checked with # the set naming style. #class-const-rgx= # Naming style matching correct class names. class-naming-style=PascalCase # Regular expression matching correct class names. Overrides class-naming- # style. If left empty, class names will be checked with the set naming style. #class-rgx= # Naming style matching correct constant names. const-naming-style=snake_case # Regular expression matching correct constant names. Overrides const-naming- # style. If left empty, constant names will be checked with the set naming # style. #const-rgx= # Minimum line length for functions/classes that require docstrings, shorter # ones are exempt. docstring-min-length=-1 # Naming style matching correct function names. function-naming-style=snake_case # Regular expression matching correct function names. Overrides function- # naming-style. If left empty, function names will be checked with the set # naming style. #function-rgx= # Good variable names which should always be accepted, separated by a comma. good-names=i, j, k, ex, Run, _ # Good variable names regexes, separated by a comma. If names match any regex, # they will always be accepted good-names-rgxs= # Include a hint for the correct naming format with invalid-name. include-naming-hint=no # Naming style matching correct inline iteration names. inlinevar-naming-style=any # Regular expression matching correct inline iteration names. Overrides # inlinevar-naming-style. If left empty, inline iteration names will be checked # with the set naming style. #inlinevar-rgx= # Naming style matching correct method names. method-naming-style=snake_case # Regular expression matching correct method names. Overrides method-naming- # style. If left empty, method names will be checked with the set naming style. #method-rgx= # Naming style matching correct module names. module-naming-style=snake_case # Regular expression matching correct module names. Overrides module-naming- # style. If left empty, module names will be checked with the set naming style. #module-rgx= # Colon-delimited sets of names that determine each other's naming style when # the name regexes allow several styles. name-group= # Regular expression which should only match function or class names that do # not require a docstring. no-docstring-rgx=^_ # List of decorators that produce properties, such as abc.abstractproperty. Add # to this list to register other decorators that produce valid properties. # These decorators are taken in consideration only for invalid-name. property-classes=abc.abstractproperty # Regular expression matching correct type variable names. If left empty, type # variable names will be checked with the set naming style. #typevar-rgx= # Naming style matching correct variable names. variable-naming-style=snake_case # Regular expression matching correct variable names. Overrides variable- # naming-style. If left empty, variable names will be checked with the set # naming style. #variable-rgx= [CLASSES] # Warn about protected attribute access inside special methods check-protected-access-in-special-methods=no # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__, __new__, setUp, __post_init__ # List of member names, which should be excluded from the protected access # warning. exclude-protected=_asdict, _fields, _replace, _source, _make # List of valid names for the first argument in a class method. valid-classmethod-first-arg=cls # List of valid names for the first argument in a metaclass class method. valid-metaclass-classmethod-first-arg=cls [DESIGN] # List of regular expressions of class ancestor names to ignore when counting # public methods (see R0903) exclude-too-few-public-methods= # List of qualified class names to ignore when counting class parents (see # R0901) ignored-parents= # Maximum number of arguments for function / method. max-args=8 # Maximum number of attributes for a class (see R0902). max-attributes=7 # Maximum number of boolean expressions in an if statement (see R0916). max-bool-expr=5 # Maximum number of branch for function / method body. max-branches=12 # Maximum number of locals for function / method body. max-locals=15 # Maximum number of parents for a class (see R0901). max-parents=10 # Maximum number of public methods for a class (see R0904). max-public-methods=20 # Maximum number of return / yield for function / method body. max-returns=6 # Maximum number of statements in function / method body. max-statements=50 # Minimum number of public methods for a class (see R0903). min-public-methods=1 [EXCEPTIONS] # Exceptions that will emit a warning when caught. overgeneral-exceptions=builtins.BaseException, builtins.Exception [FORMAT] # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. expected-line-ending-format= # Regexp for a line that is allowed to be longer than the limit. ignore-long-lines=^\s*(# )??$ # Number of spaces of indent required inside a hanging or continued line. indent-after-paren=4 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). indent-string=' ' # Maximum number of characters on a single line. max-line-length=120 # Maximum number of lines in a module. max-module-lines=1000 # Allow the body of a class to be on the same line as the declaration if body # contains single statement. single-line-class-stmt=no # Allow the body of an if to be on the same line as the test if there is no # else. single-line-if-stmt=no [IMPORTS] # List of modules that can be imported at any level, not just the top level # one. allow-any-import-level= # Allow wildcard imports from modules that define __all__. allow-wildcard-with-all=no # Deprecated modules which should not be used, separated by a comma. deprecated-modules= # Output a graph (.gv or any supported image format) of external dependencies # to the given file (report RP0402 must not be disabled). ext-import-graph= # Output a graph (.gv or any supported image format) of all (i.e. internal and # external) dependencies to the given file (report RP0402 must not be # disabled). import-graph= # Output a graph (.gv or any supported image format) of internal dependencies # to the given file (report RP0402 must not be disabled). int-import-graph= # Force import order to recognize a module as part of the standard # compatibility libraries. known-standard-library= # Force import order to recognize a module as part of a third party library. known-third-party=enchant # Couples of modules and preferred modules, separated by a comma. preferred-modules= [LOGGING] # The type of string formatting that logging methods do. `old` means using % # formatting, `new` is for `{}` formatting. logging-format-style=old # Logging modules to check that the string format arguments are in logging # function parameter format. logging-modules=logging [MESSAGES CONTROL] # Only show warnings with the listed confidence levels. Leave empty to show # all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, # UNDEFINED. confidence=HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, UNDEFINED # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this # option multiple times (only on the command line, not in the configuration # file where it should appear only once). You can also use "--disable=all" to # disable everything first and then re-enable specific checks. For example, if # you want to run only the similarities checker, you can use "--disable=all # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". disable=raw-checker-failed, bad-inline-option, locally-disabled, file-ignored, suppressed-message, useless-suppression, deprecated-pragma, use-symbolic-message-instead, duplicate-code # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). See also the "--disable" option for examples. enable=c-extension-no-member [METHOD_ARGS] # List of qualified names (i.e., library.method) which require a timeout # parameter e.g. 'requests.api.get,requests.api.post' timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. notes=FIXME, XXX, TODO # Regular expression of note tags to take in consideration. notes-rgx= [REFACTORING] # Maximum number of nested blocks for function / method body max-nested-blocks=8 # Complete name of functions that never returns. When checking for # inconsistent-return-statements if a never returning function is called then # it will be considered as an explicit return statement and no message will be # printed. never-returning-functions=sys.exit,argparse.parse_error [REPORTS] # Python expression which should return a score less than or equal to 10. You # have access to the variables 'fatal', 'error', 'warning', 'refactor', # 'convention', and 'info' which contain the number of messages in each # category, as well as 'statement' which is the total number of statements # analyzed. This score is used by the global evaluation report (RP0004). evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) # Template used to display messages. This is a python new-style format string # used to format the message information. See doc for all details. msg-template= # Set the output format. Available formats are text, parseable, colorized, json # and msvs (visual studio). You can also give a reporter class, e.g. # mypackage.mymodule.MyReporterClass. #output-format= # Tells whether to display a full report or only the messages. reports=no # Activate the evaluation score. score=yes [SIMILARITIES] # Comments are removed from the similarity computation ignore-comments=yes # Docstrings are removed from the similarity computation ignore-docstrings=yes # Imports are removed from the similarity computation ignore-imports=yes # Signatures are removed from the similarity computation ignore-signatures=yes # Minimum lines number of a similarity. min-similarity-lines=4 [SPELLING] # Limits count of emitted suggestions for spelling mistakes. max-spelling-suggestions=4 # Spelling dictionary name. Available dictionaries: none. To make it work, # install the 'python-enchant' package. spelling-dict= # List of comma separated words that should be considered directives if they # appear at the beginning of a comment and should not be checked. spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: # List of comma separated words that should not be checked. spelling-ignore-words= # A path to a file that contains the private dictionary; one word per line. spelling-private-dict-file= # Tells whether to store unknown words to the private dictionary (see the # --spelling-private-dict-file option) instead of raising a message. spelling-store-unknown-words=no [STRING] # This flag controls whether inconsistent-quotes generates a warning when the # character used as a quote delimiter is used inconsistently within a module. check-quote-consistency=no # This flag controls whether the implicit-str-concat should generate a warning # on implicit string concatenation in sequences defined over several lines. check-str-concat-over-line-jumps=no [TYPECHECK] # List of decorators that produce context managers, such as # contextlib.contextmanager. Add to this list to register other decorators that # produce valid context managers. contextmanager-decorators=contextlib.contextmanager # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E1101 when accessed. Python regular # expressions are accepted. generated-members= # Tells whether to warn about missing members when the owner of the attribute # is inferred to be None. ignore-none=yes # This flag controls whether pylint should warn about no-member and similar # checks whenever an opaque object is returned when inferring. The inference # can return multiple potential results while evaluating a Python object, but # some branches might not be evaluated, which results in partial inference. In # that case, it might be useful to still emit no-member and other checks for # the rest of the inferred objects. ignore-on-opaque-inference=yes # List of symbolic message names to ignore for Mixin members. ignored-checks-for-mixins=no-member, not-async-context-manager, not-context-manager, attribute-defined-outside-init # List of class names for which member attributes should not be checked (useful # for classes with dynamically set attributes). This supports the use of # qualified names. ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace # Show a hint with possible names when a member name was not found. The aspect # of finding the hint is based on edit distance. missing-member-hint=yes # The minimum edit distance a name should have in order to be considered a # similar match for a missing member name. missing-member-hint-distance=1 # The total number of similar names that should be taken in consideration when # showing a hint for a missing member. missing-member-max-choices=1 # Regex pattern to define which classes are considered mixins. mixin-class-rgx=.*[Mm]ixin # List of decorators that change the signature of a decorated function. signature-mutators= [VARIABLES] # List of additional names supposed to be defined in builtins. Remember that # you should avoid defining new builtins when possible. additional-builtins= # Tells whether unused global variables should be treated as a violation. allow-global-unused-variables=yes # List of names allowed to shadow builtins allowed-redefined-builtins= # List of strings which can identify a callback function by name. A callback # name must start or end with one of those strings. callbacks=cb_, _cb # A regular expression matching the name of dummy variables (i.e. expected to # not be used). dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ # Argument names that match this expression will be ignored. ignored-argument-names=_.*|^ignored_|^unused_ # Tells whether we should check for unused import in __init__ files. init-import=no # List of qualified module names which can have objects that can redefine # builtins. redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io guessit-3.8.0/pyproject.toml000066400000000000000000000004721453655371700161440ustar00rootroot00000000000000[tool.semantic_release] version_variables = ["guessit/__version__.py:__version__"] commit_subject = "chore(release): release v{version}" commit_author = "github-actions " build_command = "" [tool.check-manifest] ignore = ["docs", "docs/*", ".dockerignore", "Dockerfile", "docker", "docker/*"] guessit-3.8.0/pytest.ini000066400000000000000000000001541453655371700152560ustar00rootroot00000000000000[pytest] addopts=-s --ignore=setup.py --ignore=build --ignore=docs --doctest-modules --doctest-glob='*.rst' guessit-3.8.0/requirements.txt000066400000000000000000000000711453655371700165070ustar00rootroot00000000000000# see https://caremad.io/blog/setup-vs-requirement/ -e . guessit-3.8.0/setup.py000066400000000000000000000056461453655371700147520ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import io import os import re from setuptools import setup, find_packages here = os.path.abspath(os.path.dirname(__file__)) with io.open(os.path.join(here, 'README.md'), encoding='utf-8') as f: readme = f.read() with io.open(os.path.join(here, 'CHANGELOG.md'), encoding='utf-8') as f: changelog = f.read() install_requires = ['rebulk>=3.2.0', 'babelfish>=0.6.0', 'python-dateutil', 'importlib-resources;python_version<"3.9"'] dev_require = ['tox', 'mkdocs', 'mkdocs-material', 'pyinstaller', 'wheel', 'python-semantic-release', 'twine'] tests_require = ['pytest', 'pytest-mock', 'pytest-benchmark', 'pytest-cov', 'pylint', 'PyYAML'] package_data = ['config/*', 'data/*'] entry_points = { 'console_scripts': [ 'guessit = guessit.__main__:main' ], } with io.open('guessit/__version__.py', 'r') as f: version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]$', f.read(), re.MULTILINE).group(1) args = dict(name='guessit', version=version, description='GuessIt - a library for guessing information from video filenames.', long_description=readme + '\n\n' + changelog, long_description_content_type='text/markdown', # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers=['Development Status :: 5 - Production/Stable', 'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)', 'Operating System :: OS Independent', 'Intended Audience :: Developers', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Topic :: Multimedia', 'Topic :: Software Development :: Libraries :: Python Modules' ], keywords='python library release parser name filename movies series episodes animes', author='Rémi Alvergnat', author_email='toilal.dev@gmail.com', url='https://guessit-io.github.io/guessit', download_url='https://pypi.python.org/packages/source/g/guessit/guessit-%s.tar.gz' % version, license='LGPLv3', packages=find_packages(), package_data={'guessit': package_data}, include_package_data=True, install_requires=install_requires, entry_points=entry_points, test_suite='guessit.test', zip_safe=True, extras_require={ 'test': tests_require, 'dev': dev_require }) setup(**args) guessit-3.8.0/tox.ini000066400000000000000000000003011453655371700145320ustar00rootroot00000000000000[tox] envlist = py37,py38,py39,py310,py311,py312,pypy3.8,pypy3.9,pypy3.10 [testenv] commands = {envbindir}/pip install -e .[dev,test] {envbindir}/pylint guessit {envbindir}/pytest