plexer-cli 0.0.1a0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
plexer/__init__.py ADDED
File without changes
plexer/artifact.py ADDED
@@ -0,0 +1,20 @@
1
+ """
2
+ Plexer - Normalize media files for use with Plex Media Server
3
+
4
+ Module: Artifact Class
5
+ """
6
+
7
+
8
+ class Artifact:
9
+ """
10
+ General artifact object
11
+ """
12
+
13
+ name = ""
14
+ absolute_path = ""
15
+ mime_type = ""
16
+
17
+ def __init__(self, name: str, path: str, mime_type: str) -> None:
18
+ self.name = name
19
+ self.absolute_path = path
20
+ self.mime_type = mime_type
plexer/const.py ADDED
@@ -0,0 +1,7 @@
1
+ """
2
+ Plexer - Normalize media files for use with Plex Media Server
3
+
4
+ Module: Const - collection of global variables used across the application
5
+ """
6
+
7
+ METADATA_FILE_NAME = ".plexer"
plexer/file_manager.py ADDED
@@ -0,0 +1,131 @@
1
+ """
2
+ Plexer - Normalize media files for use with Plex Media Server
3
+
4
+ Module: File Manager - code for file-related ops
5
+ """
6
+
7
+ import os
8
+ from pathlib import Path
9
+ from magic import from_file
10
+ from logzero import logger
11
+
12
+ from .artifact import Artifact
13
+ from .const import METADATA_FILE_NAME
14
+ from .metadata import Metadata
15
+
16
+
17
+ class FileManager:
18
+ """
19
+ Class used for any file-related ops
20
+ """
21
+
22
+ src_dir = ""
23
+ dst_dir = ""
24
+
25
+ def __init__(self, src_dir, dst_dir) -> None:
26
+ self.src_dir = src_dir
27
+ self.dst_dir = dst_dir
28
+
29
+ def get_artifacts(self, tgt_dir="") -> list:
30
+ """
31
+ Gather the names of all files and directories in a given directory and return as list
32
+
33
+
34
+ Target directory is the source directory by default.
35
+ """
36
+
37
+ artifacts = []
38
+ tgt_dir = tgt_dir if tgt_dir else self.src_dir
39
+
40
+ with os.scandir(tgt_dir) as sd_iter:
41
+ for artifact_entry in sd_iter:
42
+ try:
43
+ artifact_mime_type = from_file(artifact_entry.path, mime=True)
44
+ except IsADirectoryError:
45
+ artifact_mime_type = "directory"
46
+
47
+ artifacts.append(
48
+ Artifact(
49
+ name=artifact_entry.name,
50
+ path=artifact_entry.path,
51
+ mime_type=artifact_mime_type,
52
+ )
53
+ )
54
+
55
+ return artifacts
56
+
57
+ def prep_artifacts(self, artifacts: list) -> list:
58
+ """
59
+ Perform any processing needed to prepare the artifact data for further processing
60
+
61
+ Right now, this includes:
62
+ * Properly ordering artifacts such that the metadata file is first
63
+ """
64
+
65
+ for idx, artifact in enumerate(artifacts):
66
+ if artifact.name == METADATA_FILE_NAME:
67
+ # float it to the top of the artifact set
68
+ artifacts.pop(idx)
69
+ artifacts.insert(0, artifact)
70
+
71
+ break
72
+
73
+ return artifacts
74
+
75
+ def process_directory(self, dir_artifacts: list) -> None:
76
+ """
77
+ Traverse the given directory artifacts, rename the
78
+ video files accordingly, and delete everything else
79
+
80
+ NOTE: the artifact for the metadata file MUST come first
81
+ in the artifact list. Failing to do so may lead to instability
82
+ during artifact processing.
83
+ """
84
+
85
+ video_metadata = Metadata()
86
+
87
+ logger.debug("starting directory artifact processing")
88
+
89
+ for artifact in dir_artifacts:
90
+ logger.info(
91
+ "processing artifact: [ FILE: %s | PATH: %s | FILE TYPE: %s ]",
92
+ artifact.name,
93
+ artifact.absolute_path,
94
+ artifact.mime_type,
95
+ )
96
+
97
+ if artifact.mime_type == "directory":
98
+ logger.info("subdirectory found, processing")
99
+
100
+ # start recursive subprocessing
101
+ new_dir_artifacts = self.get_artifacts(tgt_dir=artifact.absolute_path)
102
+ self.process_directory(dir_artifacts=new_dir_artifacts)
103
+ elif artifact.name == METADATA_FILE_NAME:
104
+ # read in video metadata
105
+ logger.info("metadata file found, importing")
106
+
107
+ video_metadata.import_metadata_from_file(
108
+ file_path=artifact.absolute_path
109
+ )
110
+ elif artifact.mime_type.startswith("video/"):
111
+ # move + rename
112
+ logger.info("video file found, renaming")
113
+
114
+ file_path = Path(artifact.absolute_path)
115
+ file_dir = file_path.parent
116
+ file_ext = file_path.suffix
117
+
118
+ src_file = artifact.absolute_path
119
+ dst_file = (
120
+ f"{file_dir}/{video_metadata.name}"
121
+ f" ({video_metadata.release_year}){file_ext}"
122
+ )
123
+
124
+ logger.debug("moving %s to %s", src_file, dst_file)
125
+
126
+ os.rename(src_file, dst_file)
127
+ else:
128
+ # delete the file
129
+ logger.info("unnecessary file found, deleting")
130
+
131
+ os.remove(artifact.absolute_path)
plexer/metadata.py ADDED
@@ -0,0 +1,41 @@
1
+ """
2
+ Plexer - Normalize media files for use with Plex Media Server
3
+
4
+ Module: Metadata - code for analyzing and managing video metadata
5
+ """
6
+
7
+ import json
8
+ from logzero import logger
9
+
10
+
11
+ class Metadata:
12
+ """
13
+ Code used for managing the metadata of a given video file, especially names
14
+ """
15
+
16
+ name = ""
17
+ release_year = 0
18
+
19
+ def __init__(self, name="", release_year=1900) -> None:
20
+ self.name = name
21
+ self.release_year = release_year
22
+
23
+ def import_metadata_from_file(self, file_path: str) -> None:
24
+ """
25
+ Read in given file and process data into metadata values
26
+ """
27
+
28
+ logger.debug("metadata file found @ %s - importing data", file_path)
29
+
30
+ with open(file_path, mode="r", encoding="utf-8") as metadata_file:
31
+ imported_metadata = json.load(metadata_file)
32
+
33
+ logger.debug("data imported as: %s", imported_metadata)
34
+
35
+ try:
36
+ self.name = imported_metadata["name"]
37
+ self.release_year = imported_metadata["release_year"]
38
+ except KeyError as e:
39
+ logger.error(
40
+ 'data missing in metadata file; "%s" field was not found', e.args[0]
41
+ )
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.3
2
+ Name: plexer-cli
3
+ Version: 0.0.1a0
4
+ Summary: Normalize media files for use with Plex Media Server
5
+ Project-URL: Repository, https://github.com/magneticstain/plexer.git
6
+ Project-URL: Documentation, https://github.com/magneticstain/plexer#readme
7
+ Project-URL: Issues, https://github.com/magneticstain/plexer/issues
8
+ Project-URL: Source, https://github.com/magneticstain/plexer
9
+ Author-email: Josh Carlson <837837+magneticstain@users.noreply.github.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: catagorization,formatting,media,organization,plex
13
+ Classifier: Development Status :: 2 - Pre-Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Framework :: Hatch
16
+ Classifier: Framework :: Pytest
17
+ Classifier: Framework :: tox
18
+ Classifier: Intended Audience :: System Administrators
19
+ Classifier: License :: OSI Approved :: MIT License
20
+ Classifier: Operating System :: Unix
21
+ Classifier: Programming Language :: Python
22
+ Classifier: Programming Language :: Python :: 3.9
23
+ Classifier: Programming Language :: Python :: 3.10
24
+ Classifier: Programming Language :: Python :: 3.11
25
+ Classifier: Programming Language :: Python :: 3.12
26
+ Classifier: Topic :: Utilities
27
+ Requires-Python: >=3.9
28
+ Requires-Dist: logzero~=1.7.0
29
+ Requires-Dist: python-magic~=0.4.27
30
+ Description-Content-Type: text/markdown
31
+
32
+ # Plexer
33
+
34
+ Normalize media files for use with Plex Media Server.
35
+
36
+ ![GitHub License](https://img.shields.io/github/license/magneticstain/plexer)
37
+ ![Supported Python Versions](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fmagneticstain%2Fplexer%2Frefs%2Fheads%2Fmain%2Fpyproject.toml)
38
+
39
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/magneticstain/plexer/badge)](https://scorecard.dev/viewer/?uri=github.com/magneticstain/plexer)
40
+ [![Codacy Badge](https://app.codacy.com/project/badge/Grade/38b2a65ed9ac4c85afc98e259d73474f)](https://app.codacy.com/gh/magneticstain/plexer/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
41
+ [![Run Full Suite of Checks and Tests](https://github.com/magneticstain/plexer/actions/workflows/run_full_test_suite.yml/badge.svg)](https://github.com/magneticstain/plexer/actions/workflows/run_full_test_suite.yml)
42
+
43
+ ![GitHub Issues or Pull Requests](https://img.shields.io/github/issues/magneticstain/plexer)
44
+ ![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/magneticstain/plexer/total)
45
+
46
+ [![Hatch project](https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg)](https://github.com/pypa/hatch)
47
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
48
+
49
+ ## Requirements
50
+
51
+ ### Software Dependencies
52
+
53
+ Start by creating a virtual environment and installing the required packages. Typically that looks something like:
54
+
55
+ ```bash
56
+ python -m venv .venv
57
+ source .venv/bin/activate
58
+ pip install -r requirements.txt
59
+ ```
60
+
61
+ ### Media Metadata
62
+
63
+ The biggest requirement before running plexer is to ensure that you've created a `.plexer` file in each of your target directories.
64
+
65
+ This is a JSON-formatted file that includes the movie metadata required by Plexer to perform its jobs.
66
+
67
+ #### Plexer File Generator
68
+
69
+ To easily create the `.plexer` file, you can use the one-liner below while in the movie's directory:
70
+
71
+ ```bash
72
+ echo -n "Media Name: ";read MEDIA_NAME;echo -n "Release Year (YYYY): ";read RELEASE_YEAR;echo "{\"name\": \"${MEDIA_NAME}\", \"release_year\": \"${RELEASE_YEAR}\"}" > .plexer
73
+ ```
74
+
75
+ It can be modified to support different types of media as well.
76
+
77
+ ## Usage
78
+
79
+ The source directory is the directory containing the raw media. The destination is where you'd like to save the processed media to.
80
+
81
+ ```text
82
+ usage: plexer.py [-h] [-v] [--version] -s SOURCE_DIR -d DESTINATION_DIR
83
+
84
+ options:
85
+ -h, --help show this help message and exit
86
+ -v, --verbose Verbosity (-v, -vv, etc)
87
+ --version show program's version number and exit
88
+ -s SOURCE_DIR, --source-dir SOURCE_DIR
89
+ -d DESTINATION_DIR, --destination-dir DESTINATION_DIR
90
+ ```
91
+
92
+ ## Development
93
+
94
+ ### Software Stack
95
+
96
+ For developing with Plexer, there are several tools that are in use:
97
+
98
+ 1. Build Backend, Packaging, and Dependency Management:
99
+ 1. [Hatch](https://hatch.pypa.io/1.12/)
100
+ 1. Analysis Tools:
101
+ 1. [Ruff](https://docs.astral.sh/ruff/)
102
+ 1. [Codacy](https://app.codacy.com/gh/magneticstain/plexer/dashboard)
103
+ 1. Testing:
104
+ 1. [Pytest](https://docs.pytest.org/en/latest/)
105
+ 1. [Tox](https://tox.wiki/en/stable/)
@@ -0,0 +1,9 @@
1
+ plexer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ plexer/artifact.py,sha256=JnaWeEM9msrs_EZpfGn7Xr1TcG8A7mUG-7BXuKNwXdU,378
3
+ plexer/const.py,sha256=U49nn11PvkOcvUf5eg1_hsYs12ULg_f2VtL81hWje-w,178
4
+ plexer/file_manager.py,sha256=nfmBnQXK8JWPp4INfLE6kVqeD6NMxFnn1YoTmywI4Dg,4153
5
+ plexer/metadata.py,sha256=UQ0nazq3agv7-h4NiVHNlpf2z_6rPLCBQ3TfIXKn0F8,1170
6
+ plexer_cli-0.0.1a0.dist-info/METADATA,sha256=pBZ0Yz7v9GmKW90fqiXHR7kRTuynuQDDnqYZNQLXv6U,4562
7
+ plexer_cli-0.0.1a0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
8
+ plexer_cli-0.0.1a0.dist-info/licenses/LICENSE,sha256=cs3weOTez4n04m4foiWXOU6C6Pr83ORocewlbPDaJGs,1069
9
+ plexer_cli-0.0.1a0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.25.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Josh Carlson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.