diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..2ea7395 --- /dev/null +++ b/.flake8 @@ -0,0 +1,6 @@ +[flake8] +filename = + *.py +max-line-length = 120 +extend-exclude = + venv/ diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..b67ea63 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,38 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +version: 2 +updates: + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "daily" + time: "00:00" + target-branch: "nightly" + open-pull-requests-limit: 10 + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + time: "00:00" + target-branch: "nightly" + open-pull-requests-limit: 10 + + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "daily" + time: "00:00" + target-branch: "nightly" + open-pull-requests-limit: 10 + + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "daily" + time: "00:00" + target-branch: "nightly" + open-pull-requests-limit: 10 diff --git a/.github/label-actions.yml b/.github/label-actions.yml new file mode 100644 index 0000000..2949601 --- /dev/null +++ b/.github/label-actions.yml @@ -0,0 +1,49 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +# Configuration for Label Actions - https://github.com/dessant/label-actions + +added: + comment: > + This feature has been added and will be available in the next release. +fixed: + comment: > + This issue has been fixed and will be available in the next release. +invalid:duplicate: + comment: > + :wave: @{issue-author}, this appears to be a duplicate of a pre-existing issue. + close: true + lock: true + unlabel: 'status:awaiting-triage' + +-invalid:duplicate: + reopen: true + unlock: true + +invalid:support: + comment: > + :wave: @{issue-author}, we use the issue tracker exclusively for bug reports. + However, this issue appears to be a support request. Please use our + [Support Center](https://app.lizardbyte.dev/support) for support issues. Thanks. + close: true + lock: true + lock-reason: 'off-topic' + unlabel: 'status:awaiting-triage' + +-invalid:support: + reopen: true + unlock: true + +invalid:template-incomplete: + issues: + comment: > + :wave: @{issue-author}, please edit your issue to complete the template with + all the required info. Your issue will be automatically closed in 5 days if + the template is not completed. Thanks. + prs: + comment: > + :wave: @{issue-author}, please edit your PR to complete the template with + all the required info. Your PR will be automatically closed in 5 days if + the template is not completed. Thanks. diff --git a/.github/pr_release_template.md b/.github/pr_release_template.md new file mode 100644 index 0000000..7c96c6b --- /dev/null +++ b/.github/pr_release_template.md @@ -0,0 +1,24 @@ +## Description + +This PR was created automatically. + + +### Screenshot + + + +### Issues Fixed or Closed + + + + + +## Type of Change +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Documentation update (changes to documentation) +- [ ] Repository update (changes to repository files) + +## Changelog Summary + diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..f9864d3 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,106 @@ +--- +name: CI + +on: + pull_request: + branches: [master, nightly] + types: [opened, synchronize, reopened] + push: + branches: [master] + workflow_dispatch: + +jobs: + check_changelog: + name: Check Changelog + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Verify Changelog + id: verify_changelog + if: ${{ github.ref == 'refs/heads/master' || github.base_ref == 'master' }} + # base_ref for pull request check, ref for push + uses: LizardByte/actions/verify_changelog@master + with: + token: ${{ secrets.GITHUB_TOKEN }} + outputs: + next_version: ${{ steps.verify_changelog.outputs.changelog_parser_version }} + last_version: ${{ steps.verify_changelog.outputs.latest_release_tag_name }} + release_body: ${{ steps.verify_changelog.outputs.changelog_parser_description }} + + build: + runs-on: ubuntu-20.04 + needs: check_changelog + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + path: PlexyGlass.bundle + + - name: Install Python 2.7 + uses: actions/setup-python@v3 + with: + python-version: '2.7' + + - name: Set up Python 2.7 Dependencies + working-directory: PlexyGlass.bundle + run: | + echo "Installing Requirements" + python --version + python -m pip --no-python-version-warning --disable-pip-version-check install --upgrade pip==20.3.4 setuptools + + # install dev requirements + python -m pip install --upgrade -r requirements-dev.txt + + # install plugin requirements + python ./scripts/install_requirements.py + + - name: Build plist + working-directory: PlexyGlass.bundle + env: + BUILD_VERSION: ${{ needs.check_changelog.outputs.next_version }} + run: | + python scripts/build_plist.py + + - name: Test Plex Plugin + working-directory: PlexyGlass.bundle + run: | + python ./Contents/Code/__init__.py + python ./Contents/Services/URL/YouTube/ServiceCode.pys + + - name: Upload Artifacts + if: ${{ github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' }} + uses: actions/upload-artifact@v3 + with: + name: PlexyGlass.bundle + if-no-files-found: error # 'warn' or 'ignore' are also available, defaults to `warn` + path: | + ${{ github.workspace }} + !**/*.git* + !**/*.pyc + !**/__pycache__ + !**/PlexyGlass.bundle/.* + !**/PlexyGlass.bundle/cache.sqlite + !**/PlexyGlass.bundle/docs + !**/PlexyGlass.bundle/scripts + + - name: Package Release + shell: bash + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + run: | + 7z "-xr!*.git*" "-xr!*.pyc" "-xr!__pycache__" "-x!.*" "-x!cache.sqlite" "-x!docs" "-x!scripts" \ + a "./PlexyGlass.bundle.zip" "PlexyGlass.bundle" + + mkdir artifacts + mv ./PlexyGlass.bundle.zip ./artifacts/ + + - name: Create Release + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + uses: LizardByte/.github/actions/create_release@master + with: + token: ${{ secrets.GH_BOT_TOKEN }} + next_version: ${{ needs.check_changelog.outputs.next_version }} + last_version: ${{ needs.check_changelog.outputs.last_version }} + release_body: ${{ needs.check_changelog.outputs.release_body }} diff --git a/.github/workflows/auto-create-pr.yml b/.github/workflows/auto-create-pr.yml new file mode 100644 index 0000000..ef19e40 --- /dev/null +++ b/.github/workflows/auto-create-pr.yml @@ -0,0 +1,31 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +name: Auto create PR + +on: + push: + branches: + - 'nightly' + +jobs: + create_pr: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Create Pull Request + uses: repo-sync/pull-request@v2 + with: + source_branch: "" # should be "nightly" as it's the triggering branch + destination_branch: "master" + pr_title: "Pulling ${{ github.ref_name }} into master" + pr_template: ".github/pr_release_template.md" + pr_assignee: "${{ secrets.GH_BOT_NAME }}" + pr_draft: true + pr_allow_empty: false + github_token: ${{ secrets.GH_BOT_TOKEN }} diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml new file mode 100644 index 0000000..7ff83e0 --- /dev/null +++ b/.github/workflows/automerge.yml @@ -0,0 +1,59 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +name: Automerge PR + +on: + pull_request: + types: + - opened + - synchronize + +jobs: + autoapprove: + if: > + contains(fromJson('["LizardByte-bot"]'), github.event.pull_request.user.login) && + contains(fromJson('["LizardByte-bot"]'), github.actor) + runs-on: ubuntu-latest + steps: + - name: Autoapproving + uses: hmarr/auto-approve-action@v2 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + + - name: Label autoapproved + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_BOT_TOKEN }} + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['autoapproved', 'autoupdate'] + }) + + automerge: + needs: [autoapprove] + runs-on: ubuntu-latest + concurrency: + group: automerge-${{ github.ref }} + cancel-in-progress: true + + steps: + - name: Automerging + uses: pascalgn/automerge-action@v0.15.3 + env: + BASE_BRANCHES: nightly + GITHUB_TOKEN: ${{ secrets.GH_BOT_TOKEN }} + GITHUB_LOGIN: ${{ secrets.GH_BOT_NAME }} + MERGE_LABELS: "" + MERGE_METHOD: "squash" + MERGE_COMMIT_MESSAGE: "{pullRequest.title} (#{pullRequest.number})" + MERGE_DELETE_BRANCH: true + MERGE_ERROR_FAIL: true + MERGE_FILTER_AUTHOR: ${{ secrets.GH_BOT_NAME }} + MERGE_RETRIES: "240" # 1 hour + MERGE_RETRY_SLEEP: "15000" # 15 seconds diff --git a/.github/workflows/autoupdate.yml b/.github/workflows/autoupdate.yml new file mode 100644 index 0000000..f32e65c --- /dev/null +++ b/.github/workflows/autoupdate.yml @@ -0,0 +1,32 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +# This workflow is designed to work with: +# - automerge workflows + +# It uses GitHub Action that auto-updates pull requests branches, when changes are pushed to their destination branch. +# Auto-updating to the latest destination branch works only in the context of upstream repo and not forks. + +name: autoupdate + +on: + push: + branches: + - 'nightly' + +jobs: + autoupdate-for-bot: + name: Autoupdate autoapproved PR created in the upstream + if: startsWith(github.repository, 'LizardByte/') + runs-on: ubuntu-latest + steps: + - name: Update + uses: docker://chinthakagodawita/autoupdate-action:v1 + env: + GITHUB_TOKEN: '${{ secrets.GH_BOT_TOKEN }}' + PR_FILTER: "labelled" + PR_LABELS: "autoupdate" + PR_READY_STATE: "ready_for_review" + MERGE_CONFLICT_ACTION: "ignore" diff --git a/.github/workflows/issues-stale.yml b/.github/workflows/issues-stale.yml new file mode 100644 index 0000000..d6e63e7 --- /dev/null +++ b/.github/workflows/issues-stale.yml @@ -0,0 +1,56 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +name: Stale Issues / PRs + +on: + schedule: + - cron: '00 00 * * *' + +jobs: + stale: + name: Check Stale Issues / PRs + runs-on: ubuntu-latest + steps: + - name: Stale + uses: actions/stale@v5 + with: + close-issue-message: > + This issue was closed because it has been stalled for 10 days with no activity. + close-pr-message: > + This PR was closed because it has been stalled for 10 days with no activity. + days-before-stale: 90 + days-before-close: 10 + exempt-all-assignees: true + exempt-issue-labels: 'added,fixed' + exempt-pr-labels: 'dependencies,l10n' + stale-issue-label: 'stale' + stale-issue-message: > + This issue is stale because it has been open for 90 days with no activity. + Comment or remove the stale label, otherwise this will be closed in 10 days. + stale-pr-label: 'stale' + stale-pr-message: > + This PR is stale because it has been open for 90 days with no activity. + Comment or remove the stale label, otherwise this will be closed in 10 days. + repo-token: ${{ secrets.GH_BOT_TOKEN }} + + - name: Invalid Template + uses: actions/stale@v5 + with: + close-issue-message: > + This issue was closed because the the template was not completed after 5 days. + close-pr-message: > + This PR was closed because the the template was not completed after 5 days. + days-before-stale: 0 + days-before-close: 5 + exempt-pr-labels: 'dependencies,l10n' + only-labels: 'invalid:template-incomplete' + stale-issue-label: 'invalid:template-incomplete' + stale-issue-message: > + Invalid issues template. + stale-pr-label: 'invalid:template-incomplete' + stale-pr-message: > + Invalid PR template. + repo-token: ${{ secrets.GH_BOT_TOKEN }} diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml new file mode 100644 index 0000000..6ba4444 --- /dev/null +++ b/.github/workflows/issues.yml @@ -0,0 +1,22 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +name: Issues + +on: + issues: + types: [labeled, unlabeled] + discussion: + types: [labeled, unlabeled] + +jobs: + label: + name: Label Actions + runs-on: ubuntu-latest + steps: + - name: Label Actions + uses: dessant/label-actions@v2 + with: + github-token: ${{ secrets.GH_BOT_TOKEN }} diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml new file mode 100644 index 0000000..0abc26b --- /dev/null +++ b/.github/workflows/pull-requests.yml @@ -0,0 +1,27 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +name: Pull Requests + +on: + pull_request_target: + types: [opened, synchronize, edited, reopened] + +jobs: + check-pull-request: + name: Check Pull Request + runs-on: ubuntu-latest + steps: + - uses: Vankka/pr-target-branch-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + target: master + exclude: nightly # Don't prevent going from nightly -> master + change-to: nightly + comment: | + Your PR was set to `master`, PRs should be sent to `nightly`. + The base branch of this PR has been automatically changed to `nightly`. + Please check that there are no merge conflicts diff --git a/.github/workflows/python-flake8.yml b/.github/workflows/python-flake8.yml new file mode 100644 index 0000000..463fb8a --- /dev/null +++ b/.github/workflows/python-flake8.yml @@ -0,0 +1,31 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +name: flake8 + +on: + pull_request: + branches: [master, nightly] + types: [opened, synchronize, reopened] + +jobs: + flake8: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 # https://github.com/actions/setup-python + with: + python-version: '3.10' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools flake8 + + - name: Test with flake8 + run: | + python -m flake8 --verbose diff --git a/.github/workflows/yaml-lint.yml b/.github/workflows/yaml-lint.yml new file mode 100644 index 0000000..f2e0721 --- /dev/null +++ b/.github/workflows/yaml-lint.yml @@ -0,0 +1,46 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +name: yaml lint + +on: + pull_request: + branches: [master, nightly] + types: [opened, synchronize, reopened] + +jobs: + yaml-lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: yaml lint + id: yaml-lint + uses: ibiqlik/action-yamllint@v3 + with: + # https://yamllint.readthedocs.io/en/stable/configuration.html#default-configuration + config_data: | + extends: default + rules: + comments: + level: error + line-length: + max: 120 + truthy: + allowed-values: ['true', 'false', 'on'] # GitHub uses "on" for workflow event triggers + check-keys: true + level: error + + - name: Log + run: | + echo ${{ steps.yaml-lint.outputs.logfile }} + + - name: Upload logs + uses: actions/upload-artifact@v3 + if: failure() + with: + name: yamllint-logfile + path: ${{ steps.yaml-lint.outputs.logfile }} diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..086d61f --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,28 @@ +--- +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python +build: + os: ubuntu-20.04 + tools: + python: "2.7" + +# Build documentation in the docs/ directory with Sphinx +sphinx: + builder: html + configuration: docs/source/conf.py + fail_on_warning: true + +# Using Sphinx, build docs in additional formats +formats: all + +python: + install: + - requirements: requirements.txt # plugin requirements + - requirements: requirements-dev.txt # docs requirements + system_packages: true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..66b965b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## [0.0.1] - 2022-09-09 +### Added +- Initial Release diff --git a/Contents/Services/URL/YouTube/ServiceCode.pys b/Contents/Services/URL/YouTube/ServiceCode.pys index 47c04c4..e4b44e7 100644 --- a/Contents/Services/URL/YouTube/ServiceCode.pys +++ b/Contents/Services/URL/YouTube/ServiceCode.pys @@ -8,6 +8,9 @@ import sys # plex debugging if 'plexscripthost' not in sys.executable.lower(): # the code is running outside of Plex + from plexhints import update_sys_path + update_sys_path() + from plexhints.decorator_kit import indirect # decorator kit from plexhints.exception_kit import Ex # exception kit from plexhints.log_kit import Log # log kit @@ -18,6 +21,11 @@ if 'plexscripthost' not in sys.executable.lower(): # the code is running outsid # lib imports import youtube_dl +# constants +plugin_name = 'PlexyGlass' +service_name = 'YouTube' +service_type = 'URL' + # todo - add more formats # determine if it's possible to add individual audio and video streams... # will plex automatically select one of each to combine them? @@ -90,16 +98,16 @@ def extract_youtube_data(url): download=False # We just want to extract the info ) except youtube_dl.utils.ExtractorError as e: - Log.Error(e) + Log.Error('%s :: %s %s Service :: error: %s' % (plugin_name, service_name, service_type, e)) raise Ex.MediaNotAvailable except youtube_dl.utils.DownloadError as e: print('----------') print(e) if 'Sign in to confirm your age' in str(e): - Log.Error(e) + Log.Error('%s :: %s %s Service :: error: %s' % (plugin_name, service_name, service_type, e)) raise Ex.MediaNotAuthorized elif 'The uploader has not made this video available in your country.' in str(e): - Log.Error(e) + Log.Error('%s :: %s %s Service :: error: %s' % (plugin_name, service_name, service_type, e)) raise Ex.MediaGeoblocked else: if 'entries' in result: @@ -134,16 +142,18 @@ def normalize_url(url): >>> normalize_url(url='https://www.youtube.com/watch?v=dQw4w9WgXcQ') 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' """ - Log.Info('Normalizing YouTube url: %s' % url) + Log.Info('%s :: %s %s Service :: normalizing url: %s' % (plugin_name, service_name, service_type, url)) video_data = extract_youtube_data(url=url) if video_data: try: webpage_url = video_data['webpage_url'] - Log.Info('YouTube url normalized to: %s' % webpage_url) + Log.Error('%s :: %s %s Service :: normalized url to: %s' % ( + plugin_name, service_name, service_type, webpage_url)) except KeyError: - Log.Error('YouTube webpage_url not found in video_data: %s' % video_data) + Log.Error('%s :: %s %s Service :: webpage_url not found in video_data: %s' % ( + plugin_name, service_name, service_type, video_data)) return else: return webpage_url @@ -169,7 +179,7 @@ def metadata_object_for_url(url): >>> metadata_object_for_url(url='https://www.youtube.com/watch?v=dQw4w9WgXcQ') ... """ - Log.Info('Collecting metadata for YouTube url: %s' % url) + Log.Info('%s :: %s %s Service :: collecting metadata for url: %s' % (plugin_name, service_name, service_type, url)) video_data = extract_youtube_data(url=url) @@ -220,11 +230,10 @@ def metadata_object_for_url(url): # duration must be in milliseconds duration *= 1000 - Log.Info('YouTube service :: title: %s' % title) - Log.Info('YouTube service :: summary: %s' % summary) - Log.Info('YouTube service :: thumb: %s' % thumb) - Log.Info('YouTube service :: originally_available_at: %s' % date) - Log.Info('YouTube service :: duration: %s' % duration) + Log.Info('%s :: %s %s Service :: title: %s' % (plugin_name, service_name, service_type, title)) + Log.Info('%s :: %s %s Service :: summary: %s' % (plugin_name, service_name, service_type, summary)) + Log.Info('%s :: %s %s Service :: originally_available_at: %s' % (plugin_name, service_name, service_type, date)) + Log.Info('%s :: %s %s Service :: duration: %s' % (plugin_name, service_name, service_type, duration)) return VideoClipObject( title=title, @@ -255,7 +264,8 @@ def media_objects_for_url(url): >>> media_objects_for_url(url='https://www.youtube.com/watch?v=dQw4w9WgXcQ') [...] """ - Log.Info('Attempting to create media objects for url: %s' % url) + Log.Info('%s :: %s %s Service :: attempting to create media object for url: %s' % ( + plugin_name, service_name, service_type, url)) video_data = extract_youtube_data(url=url) ret = [] @@ -264,9 +274,9 @@ def media_objects_for_url(url): for fmt in video_data['formats']: fmt_id = int(fmt['format_id']) # youtube-dl gives unicode values - Log.Debug('YouTube service :: processing format_id: %s' % fmt_id) if fmt_id in format_dict: # item has video and audio! - Log.Info('YouTube Service: found matching format id: %s' % fmt_id) + Log.Info('%s :: %s %s Service :: found matching format id: %s' % ( + plugin_name, service_name, service_type, fmt_id)) ret.append(MediaObject( parts=[ PartObject( @@ -309,7 +319,7 @@ def play_video(url=None, default_fmt=None, **kwargs): >>> play_video(url='https://www.youtube.com/watch?v=dQw4w9WgXcQ', default_fmt=37) ... """ - Log.Info('YouTube service :: attempting to play video: %s' % url) + Log.Info('%s :: %s %s Service :: attempting to play video: %s' % (plugin_name, service_name, service_type, url)) if not url: return None @@ -319,10 +329,9 @@ def play_video(url=None, default_fmt=None, **kwargs): if video_data: for fmt in video_data['formats']: fmt_id = int(fmt['format_id']) # youtube-dl gives unicode values - Log.Debug('YouTube service :: processing format_id: %s' % fmt_id) if fmt_id == default_fmt: final_url = fmt['url'] - Log.Info('YouTube service :: final_url: %s' % final_url) + Log.Info('%s :: %s %s Service :: final_url: %s' % (plugin_name, service_name, service_type, final_url)) return IndirectResponse(VideoClipObject, key=final_url) diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..eebfc77 --- /dev/null +++ b/README.rst @@ -0,0 +1,28 @@ +:github_url: https://github.com/LizardByte/PlexyGlass/tree/nightly/README.rst + +Overview +======== +LizardByte has the full documentation hosted on `Read the Docs `_. + +About +----- +PlexyGlass is a Services plug-in for Plex Media Player. The plug-in currently provides a YouTube URL Service. +Additional services may be added in the future. + +Integrations +------------ + +.. image:: https://img.shields.io/github/workflow/status/lizardbyte/plexyglass/CI/master?label=CI%20build&logo=github&style=for-the-badge + :alt: GitHub Workflow Status (CI) + :target: https://github.com/LizardByte/PlexyGlass/actions/workflows/CI.yml?query=branch%3Amaster + +.. image:: https://img.shields.io/readthedocs/plexyglass?label=Docs&style=for-the-badge&logo=readthedocs + :alt: Read the Docs + :target: http://plexyglass.readthedocs.io/ + +Downloads +--------- + +.. image:: https://img.shields.io/github/downloads/lizardbyte/plexyglass/total?style=for-the-badge&logo=github + :alt: GitHub Releases + :target: https://github.com/LizardByte/PlexyGlass/releases/latest diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..dc1312a --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/source/about/installation.rst b/docs/source/about/installation.rst new file mode 100644 index 0000000..cdb36b8 --- /dev/null +++ b/docs/source/about/installation.rst @@ -0,0 +1,11 @@ +:github_url: https://github.com/LizardByte/PlexyGlass/tree/nightly/docs/source/about/installation.rst + +Installation +============ +#. Download the ``plexyglass.bundle.zip`` from the latest + `releases `_ +#. Extract the contents to your Plex Media Server Plugins directory. + +.. Tip:: See + `How do I find the Plug-Ins folder `_ + for information specific to your Plex server install. diff --git a/docs/source/about/overview.rst b/docs/source/about/overview.rst new file mode 100644 index 0000000..ec7f524 --- /dev/null +++ b/docs/source/about/overview.rst @@ -0,0 +1 @@ +.. include:: ../../../README.rst \ No newline at end of file diff --git a/docs/source/about/usage.rst b/docs/source/about/usage.rst new file mode 100644 index 0000000..b22a0cf --- /dev/null +++ b/docs/source/about/usage.rst @@ -0,0 +1,34 @@ +:github_url: https://github.com/LizardByte/PlexyGlass/tree/nightly/docs/source/about/usage.rst + +Usage +===== + +End Users +--------- +There are available preferences/settings available. Navigate to the `Plugins` menu within the Plex server settings. +Select the gear cog when hovering over the PlexyGlass plugin tile. + +Developers +---------- +This section is intended for developers utilizing the plugin to support URL services or the like. + +It is very easy to use the URL service in your metadata agent. Below is an example. + +.. code-block:: python + + video_title='Rick Astley - Never Gonna Give You Up (Official Music Video)' + video_url='https://www.youtube.com/watch?v=dQw4w9WgXcQ' + + metadata.extras.add(OtherObject(title=video_title, url=video_url)) + +You can pass in many other parameters if you'd like, but they are all optional except the url. Below is a bare +minimal example. + +.. code-block:: python + + video_url='https://www.youtube.com/watch?v=dQw4w9WgXcQ' + + metadata.extras.add(OtherObject(url=video_url)) + +.. Tip:: For help with metadata agent or general plug-in development, check out our + `plexhints `_ python library. diff --git a/docs/source/code_docs/main.rst b/docs/source/code_docs/main.rst new file mode 100644 index 0000000..935656d --- /dev/null +++ b/docs/source/code_docs/main.rst @@ -0,0 +1,9 @@ +:github_url: https://github.com/LizardByte/PlexyGlass/tree/nightly/Contents/Code/__init__.py + +.. include:: ../global.rst + +:modname:`__init__` +------------------------ +.. automodule:: Code + :members: + :show-inheritance: diff --git a/docs/source/code_docs/youtube.rst b/docs/source/code_docs/youtube.rst new file mode 100644 index 0000000..50d990a --- /dev/null +++ b/docs/source/code_docs/youtube.rst @@ -0,0 +1,9 @@ +:github_url: https://github.com/LizardByte/PlexyGlass/tree/nightly/Contents/Services/URL/YouTube/ServiceCode.pys + +.. include:: ../global.rst + +:modname:`YouTube` +------------------------ +.. automodule:: YouTube + :members: + :show-inheritance: diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..5b61f42 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,136 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# standard imports +from datetime import datetime +import os +import re +import shutil +import sys + +try: + import pathlib2 as pathlib +except ImportError: + import pathlib + + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. + +script_dir = os.path.dirname(os.path.abspath(__file__)) # the directory of this file +source_dir = os.path.dirname(script_dir) # the source folder directory +root_dir = os.path.dirname(source_dir) # the root folder directory + +# destination for copies of services +service_destination_directory = os.path.join(source_dir, 'build', 'service_modules') +service_destination_path = pathlib.Path(service_destination_directory) +service_destination_path.mkdir(parents=True, exist_ok=True) + + +paths = [ + os.path.join(root_dir, 'Contents', 'Libraries', 'Shared'), # location of plugin dependencies + os.path.join(root_dir, 'Contents'), # location of "Code" module, aka the Plugin + service_destination_directory, # location of copied service files +] + +# Copy ServiceCode files to python modules +services_dir = os.path.join(root_dir, 'Contents', 'Services') + +services = dict( + URL=[ + 'YouTube' + ] +) + +for service_type in services: + for service in services[service_type]: + service_file = os.path.join(services_dir, service_type, service, 'ServiceCode.pys') + print(service_file) + + if os.path.isfile(service_file): + shutil.copy(service_file, os.path.join(service_destination_directory, '%s.py' % service)) + +for directory in paths: + sys.path.insert(0, directory) + +# -- Project information ----------------------------------------------------- +project = 'PlexyGlass' +copyright = '%s, %s' % (datetime.now().year, project) +author = 'ReenigneArcher' + +# The full version, including alpha/beta/rc tags +with open(os.path.join(root_dir, 'CHANGELOG.md'), 'r') as f: + version = re.search(r"\[((\d+)\.(\d+)\.(\d+))]", str(f.read())).group(1) + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + # 'm2r2', # enable markdown files + 'numpydoc', # this automatically loads `sphinx.ext.autosummary` as well + 'sphinx.ext.autodoc', # autodocument modules + 'sphinx.ext.autosectionlabel', + 'sphinx.ext.todo', # enable to-do sections + 'sphinx.ext.viewcode' # add links to view source code +] + +# Add any paths that contain templates here, relative to this directory. +# templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['toc.rst'] + +# Extensions to include. +source_suffix = ['.rst'] + +# Change default contents file +master_doc = 'index' + +# -- Options for HTML output ------------------------------------------------- + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# html_static_path = ['_static'] + +html_logo = os.path.join(root_dir, 'Contents', 'Resources', 'attribution.png') + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'sphinx_rtd_theme' + +html_theme_options = { + 'analytics_id': 'G-SSW90X5YZX', # Provided by Google in your dashboard + 'analytics_anonymize_ip': False, + 'logo_only': False, + 'display_version': True, + 'prev_next_buttons_location': 'bottom', + 'style_external_links': True, + 'vcs_pageview_mode': 'blob', + 'style_nav_header_background': '#151515', + # Toc options + 'collapse_navigation': True, + 'sticky_navigation': True, + 'navigation_depth': 4, + 'includehidden': True, + 'titles_only': False, +} + +# extension config options +autodoc_preserve_defaults = True # Do not evaluate default arguments of functions +autosectionlabel_prefix_document = True # Make sure the target is unique +todo_include_todos = True + +# numpydoc config +numpydoc_validation_checks = {'all', 'SA01'} # Report warnings for all checks *except* for SA01 diff --git a/docs/source/contributing/build.rst b/docs/source/contributing/build.rst new file mode 100644 index 0000000..80d6818 --- /dev/null +++ b/docs/source/contributing/build.rst @@ -0,0 +1,49 @@ +:github_url: https://github.com/LizardByte/PlexyGlass/tree/nightly/docs/source/contributing/build.rst + +Build +===== +Compiling PlexyGlass is fairly simple; however it is recommended to use Python 2.7 since the Plex framework is using +Python 2.7. + +Clone +----- +Ensure `git `_ is installed and run the following: + + .. code-block:: bash + + git clone https://github.com/lizardbyte/plexyglass.git plexyglass.bundle + cd ./plexyglass.bundle + +Setup venv +---------- +It is recommended to setup and activate a `venv`_. + +Install Requirements +-------------------- +Install Requirements + .. code-block:: bash + + # use python 2.7 venv + python ./scripts/install_requirements.txt + +Development Requirements + .. code-block:: bash + + python -m pip install -r ./scripts/requirements-dev.txt + +Build Plist +----------- + .. code-block:: bash + + python ./scripts/build_plist.py + +Remote Build +------------ +It may be beneficial to build remotely in some cases. This will enable easier building on different operating systems. + +#. Fork the project +#. Activate workflows +#. Trigger the `CI` workflow manually +#. Download the artifacts from the workflow run summary + +.. _venv: https://docs.python.org/3/library/venv.html diff --git a/docs/source/contributing/testing.rst b/docs/source/contributing/testing.rst new file mode 100644 index 0000000..05ebfdb --- /dev/null +++ b/docs/source/contributing/testing.rst @@ -0,0 +1,54 @@ +:github_url: https://github.com/RetroArcher/RetroArcher.bundle/tree/nightly/docs/source/contributing/testing.rst + +Testing +======= + +Flake8 +------ +PlexyGlass uses `Flake8 `_ for enforcing consistent code styling. Flake is included +in the ``requirements-dev.txt``. + +The config file for flake8 is ``.flake8``. This is already included in the root of the repo and should not be modified. + +Test with Flake8 + .. code-block:: bash + + python -m flake8 + +Sphinx +------ +PlexyGlass uses `Sphinx `_ for documentation building. Sphinx is included +in the ``requirements-dev.txt``. + +PlexyGlass follows `numpydoc `_ styling and formatting in +docstrings. This will be tested when building the docs. `numpydoc` is included in the ``requirements-dev.txt``. + +The config file for Sphinx is ``docs/source/conf.py``. This is already included in the root of the repo and should not +be modified. + +Test with Sphinx + .. code-block:: bash + + cd docs + make html + + Alternatively + + .. code-block:: bash + + cd docs + sphinx-build -b html source build + +pytest +------ +.. Todo:: PyTest is not yet implemented. + +PlexyGlass uses `pytest `_ for unit testing. pytest is included in the +``requirements-dev.txt``. + +No config is required for pytest. + +Test with pytest + .. code-block:: bash + + python -m pytest diff --git a/docs/source/global.rst b/docs/source/global.rst new file mode 100644 index 0000000..b117004 --- /dev/null +++ b/docs/source/global.rst @@ -0,0 +1,5 @@ +.. role:: modname + :class: modname + +.. role:: title + :class: title diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..009baae --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,5 @@ +:github_url: https://github.com/LizardByte/PlexyGlass/tree/nightly/docs/source/index.rst + +Table of Contents +================= +.. include:: toc.rst diff --git a/docs/source/toc.rst b/docs/source/toc.rst new file mode 100644 index 0000000..06f7dee --- /dev/null +++ b/docs/source/toc.rst @@ -0,0 +1,28 @@ +.. toctree:: + :maxdepth: 2 + :caption: About + + about/overview + about/installation + about/usage + +.. toctree:: + :maxdepth: 2 + :caption: Contributing + + contributing/build + contributing/testing + +.. toctree:: + :maxdepth: 0 + :caption: Plugin Code + :titlesonly: + + code_docs/main + +.. toctree:: + :maxdepth: 0 + :caption: URL Services Code + :titlesonly: + + code_docs/youtube diff --git a/scripts/build_plist.py b/scripts/build_plist.py index b528364..add4237 100644 --- a/scripts/build_plist.py +++ b/scripts/build_plist.py @@ -51,7 +51,7 @@ Version: %s %s - | | Releases @@ -60,7 +60,7 @@ Reference: | Docs + href="https://docs.lizardbyte.dev/projects/plexyglass" target="_blank">Docs ]]>