diff --git a/.gitignore b/.gitignore index 38e944a..16f1030 100644 --- a/.gitignore +++ b/.gitignore @@ -64,4 +64,5 @@ peertube_secret .youtube_credentials.json nfo_example.txt peertube_secret.sample -youtube_secret.json.sample \ No newline at end of file +youtube_secret.json.sample +*.mp4 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 36e16c0..aeb8b6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,13 @@ ## v0.9.0 ## Breaking changes -Now using poetry for packaging and installing! It's now easier to maintain and publish package, but means changes to use prismedia. -Prismedia is now seen as a python module, so you may now use `python -m prismedia` (or even directly `prismedia` if you install with poetry) instead of `./prismedia_upload.py`. +Now using poetry for packaging and installing! It's easier to maintain and publish package, but means changes when using prismedia from command line. +Prismedia is now seen as a python module, so you need to use `python -m prismedia` (or even directly `prismedia` if you install with poetry) instead of `./prismedia_upload.py`. ### Features - - Prismedia now uses [poetry](https://python-poetry.org) to allow easier installation usage and build, see the README - - Add two new options to schedule video by platform. You may now use youtubeAt and peertubeAt to prepare previews + - Prismedia now uses [poetry](https://python-poetry.org) to allow easier installation usage and build, see the README (fix #34) + - Add two new options to schedule video by platform. You may now use youtubeAt and peertubeAt to prepare previews (fix #43) + - Enhance the NFO system to allow a hierarchical loading of multiple NFO, with priorities. See README and [prismedia/samples](prismedia/samples) for details (fix #11) ## v0.8.0 diff --git a/README.md b/README.md index 5be531c..eb317c9 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Scripting your way to upload videos to peertube and youtube. Works with Python 3 - [Peertube](#peertube) - [Youtube](#youtube) - [Usage](#usage) +- [Enhanced use of NFO](#enhanced-use-of-nfo) - [Features](#features) - [Compatibility](#compatibility) - [Sources](#sources) @@ -149,6 +150,46 @@ Languages: Japanese, Korean, Mandarin, Portuguese, Punjabi, Russian, Spanish ``` +## Enhanced use of NFO +Since Prismedia v0.9.0, the NFO system has been improved to allow hierarchical loading. +First of all, **if you already used nfo**, either with `--nfo` or by using `videoname.txt`, nothing changes :-) + +But you are now able to use a more flexible NFO system, by using priorities. This allow you to set some defaults to avoid recreating a full nfo for each video + +Basically, Prismedia will now load options in this order, using the last value found in case of conflict: +`nfo.txt < directory_name.txt < video_name.txt < command line NFO < command line argument` + +You'll find a complete set of samples in the [prismedia/samples](prismedia/samples) directory so let's take it as an example: +``` +$ tree Recipes/ +Recipes/ +├── cli_nfo.txt +├── nfo.txt +├── samples.txt +├── yourvideo1.mp4 +├── yourvideo1.txt +├── yourvideo1.jpg +├── yourvideo2.mp4 +└── yourvideo2.txt +``` + +By using +``` +prismedia --file=/path/to/Recipes/yourvideo1.mp4 --nfo=/path/to/Recipes/cli_nfo.txt --cca +``` + +Prismedia will: +- look for options in `nfo.txt` +- look for options in `samples.txt` (from directory name) and erase any previous conflicting options +- look for options in `yourvideo1.txt` (from video name) and erase any previous conflicting options +- look for options in `cli_nfo.txt` (from the `--nfo` in command line) and erase any previous conflicting options +- erase any previous option regarding CCA as it's specified in cli with `--cca` +- take `yourvideo1.jpg` as thumbnail if no other files has been specified in previous NFO + +In other word, Prismedia will now use option given in cli, then look for option in cli_nfo.txt, then complete with video_name.txt, then directory_name.txt, and finally complete with nfo.txt + +It allows to specify more easily default options for an entire set of video, directory, playlist and so on. + ## Features - [x] Youtube upload diff --git a/poetry.lock b/poetry.lock index 1f72b3a..ccd9b43 100644 --- a/poetry.lock +++ b/poetry.lock @@ -208,7 +208,6 @@ pyasn1 = ">=0.4.6,<0.5.0" [[package]] category = "main" description = "File type identification using libmagic" -marker = "platform_system == \"Linux\"" name = "python-magic" optional = false python-versions = "*" diff --git a/prismedia/samples/cli_nfo.txt b/prismedia/samples/cli_nfo.txt new file mode 100644 index 0000000..880dd8f --- /dev/null +++ b/prismedia/samples/cli_nfo.txt @@ -0,0 +1,12 @@ +### This NFO is aimed to be passed to prismedia through the --nfo cli option ### +### eg: +### python -m prismedia --file=/path/to/yourvideo.mp4 --nfo=/path/to/cli_nfo.txt ### +### It's the more priority NFO, only erased by direct cli options ### +[video] +disable-comments = False +nsfw = True +# Publish on Peertube at a specific date +peertubeAt = 2034-05-14T19:00:00 +platform = peertube +# debug to display all loaded options +debug = True \ No newline at end of file diff --git a/prismedia/config/nfo_example.txt b/prismedia/samples/full_nfo_examples.txt similarity index 61% rename from prismedia/config/nfo_example.txt rename to prismedia/samples/full_nfo_examples.txt index a6c537d..0ee5410 100644 --- a/prismedia/config/nfo_example.txt +++ b/prismedia/samples/full_nfo_examples.txt @@ -1,16 +1,12 @@ ### This NFO example show how to construct a NFO for your video ### -### All fields are optional, but you need at least one fields (otherwise NFO is useless :-p) ### +### All fields are optionals, but you need at least one field (otherwise NFO is useless :-p) ### ### See --help for options explanation -### Prismedia will search and use NFO in this order: ### -### 1. file passed in command line through --nfo ### -### 2. file inside video directory named after --name command line option append with .txt ### -### 3. file inside video directory named after --file command line option with .txt extension ### [video] name = videoname description = Your complete video description Multilines description should be wrote with a blank space - at the beginning of the line :) + at the beginning of the line :-) tags = list of tags, comma separated category = Films cca = True @@ -24,7 +20,7 @@ playlistCreate = True nsfw = False platform = youtube, peertube language = French -publishAt=2034-05-07T19:00:00 +publishAt = 2034-05-07T19:00:00 # platformAt overrides the default publishAt for the corresponding platform #peertubeAt = 2034-05-14T19:00:00 #youtubeAt = 2034-05-21T19:00:00 \ No newline at end of file diff --git a/prismedia/samples/nfo.txt b/prismedia/samples/nfo.txt new file mode 100644 index 0000000..7b18585 --- /dev/null +++ b/prismedia/samples/nfo.txt @@ -0,0 +1,9 @@ +### This NFO is named nfo.txt and is stored in the directory of your videos ### +### This is the less priority NFO, you may use it to set default generic options ### +[video] +# Some generic options for your videos +cca = True +privacy = private +disable-comments = True +channel = DefaultChannel +channelCreate = True \ No newline at end of file diff --git a/prismedia/samples/samples.txt b/prismedia/samples/samples.txt new file mode 100644 index 0000000..338905e --- /dev/null +++ b/prismedia/samples/samples.txt @@ -0,0 +1,14 @@ +### This NFO is named from the directory where your video are. ### +### While more specific than nfo.txt, it's less priority than other NFO ### +### You may use it for options specific to videos in this directory, but still globals ### +[video] +channel = MyMoreSpecificChannel +disable-comments = False +channelCreate = True +category = Films +playlist = Desserts Recipes playlist +playlistCreate = True +nsfw = False +platform = youtube, peertube +language = French +tags = list of tags, comma separated \ No newline at end of file diff --git a/prismedia/samples/yourvideo.txt b/prismedia/samples/yourvideo.txt new file mode 100644 index 0000000..269d751 --- /dev/null +++ b/prismedia/samples/yourvideo.txt @@ -0,0 +1,14 @@ +### This NFO is named from your video name (here let's say your video is named "yourvideo.mp4") ### +### It aims to give options specific to this videos ### +[video] +disable-comments = False +#thumbnail = /path/to/your/thumbnail.jpg # Set the absolute path to your thumbnail +name = videoname +description = Your complete video description + Multilines description + should be wrote with a blank space + at the beginning of the line :-) +publishAt = 2034-05-07T19:00:00 +# platformAt overrides the default publishAt for the corresponding platform +#peertubeAt = 2034-05-14T19:00:00 +#youtubeAt = 2034-05-21T19:00:00 \ No newline at end of file diff --git a/prismedia/utils.py b/prismedia/utils.py index fca5f51..b9e4c89 100644 --- a/prismedia/utils.py +++ b/prismedia/utils.py @@ -107,6 +107,7 @@ def remove_empty_kwargs(**kwargs): good_kwargs[key] = value return good_kwargs + def searchThumbnail(options): video_directory = dirname(options.get('--file')) + "/" # First, check for thumbnail based on videoname @@ -124,73 +125,78 @@ def searchThumbnail(options): options['--thumbnail'] = video_directory + video_file + ".jpeg" return options + # return the nfo as a RawConfigParser object -def loadNFO(options): - video_directory = dirname(options.get('--file')) + "/" - if options.get('--nfo'): - try: - logging.info("Using " + options.get('--nfo') + " as NFO, loading...") - if isfile(options.get('--nfo')): - nfo = RawConfigParser() - nfo.read(options.get('--nfo'), encoding='utf-8') - return nfo - else: - logging.error("Given NFO file does not exist, please check your path.") - exit(1) - except Exception as e: - logging.error("Problem with NFO file: " + str(e)) - exit(1) - else: - if options.get('--name'): - nfo_file = video_directory + options.get('--name') + ".txt" - if isfile(nfo_file): - try: - logging.info("Using " + nfo_file + " as NFO, loading...") - nfo = RawConfigParser() - nfo.read(nfo_file, encoding='utf-8') - return nfo - except Exception as e: - logging.error("Problem with NFO file: " + str(e)) - exit(1) +def loadNFO(filename): + try: + logging.info("Loading " + filename + " as NFO") + nfo = RawConfigParser() + nfo.read(filename, encoding='utf-8') + return nfo + except Exception as e: + logging.error("Problem loading NFO file " + filename + ": " + str(e)) + exit(1) + return False + + +def parseNFO(options): + video_directory = dirname(options.get('--file')) + directory_name = basename(video_directory) + nfo_txt = False + nfo_directory = False + nfo_videoname = False + nfo_file = False + nfo_cli = False + + if isfile(video_directory + "/" + "nfo.txt"): + nfo_txt = loadNFO(video_directory + "/" + "nfo.txt") + elif isfile(video_directory + "/" + "NFO.txt"): + nfo_txt = loadNFO(video_directory + "/" + "NFO.txt") + + if isfile(video_directory + "/" + directory_name+ ".txt"): + nfo_directory = loadNFO(video_directory + "/" + directory_name+ ".txt") + + if options.get('--name'): + if isfile(video_directory + "/" + options.get('--name')): + nfo_videoname = loadNFO(video_directory + "/" + options.get('--name') + ".txt") - # if --nfo and --name does not exist, use --file as default video_file = splitext(basename(options.get('--file')))[0] - nfo_file = video_directory + video_file + ".txt" - if isfile(nfo_file): - try: - logging.info("Using " + nfo_file + " as NFO, loading...") - nfo = RawConfigParser() - nfo.read(nfo_file, encoding='utf-8') - return nfo - except Exception as e: - logging.error("Problem with nfo file: " + str(e)) + if isfile(video_directory + "/" + video_file + ".txt"): + nfo_file = loadNFO(video_directory + "/" + video_file + ".txt") + + if options.get('--nfo'): + if isfile(options.get('--nfo')): + nfo_cli = loadNFO(options.get('--nfo')) + else: + logging.error("Given NFO file does not exist, please check your path.") exit(1) - logging.info("No suitable NFO found, skipping.") - return False -def parseNFO(options): - nfo = loadNFO(options) - if nfo: - # We need to check all options and replace it with the nfo value if not defined (None or False) - for key, value in options.items(): - key = key.replace("-", "") - try: - # get string options - if value is None and nfo.get('video', key): - options['--' + key] = nfo.get('video', key) - # get boolean options - elif value is False and nfo.getboolean('video', key): - options['--' + key] = nfo.getboolean('video', key) - except NoOptionError: - continue - except NoSectionError: - logging.error("Given NFO file miss section [video], please check syntax of your NFO.") - exit(1) + # We need to load NFO in this exact order to keep the priorities + # options in cli > nfo_cli > nfo_file > nfo_videoname > nfo_directory > nfo_txt + for nfo in [nfo_cli, nfo_file, nfo_videoname, nfo_directory, nfo_txt]: + if nfo: + # We need to check all options and replace it with the nfo value if not defined (None or False) + for key, value in options.items(): + key = key.replace("-", "") + try: + # get string options + if value is None and nfo.get('video', key): + options['--' + key] = nfo.get('video', key) + # get boolean options + elif value is False and nfo.getboolean('video', key): + options['--' + key] = nfo.getboolean('video', key) + except NoOptionError: + continue + except NoSectionError: + logging.error(nfo + " misses section [video], please check syntax of your NFO.") + exit(1) return options + def upcaseFirstLetter(s): return s[0].upper() + s[1:] + def cleanString(toclean): toclean = unidecode.unidecode(toclean) cleaned = re.sub('[^A-Za-z0-9]+', '', toclean) diff --git a/pyproject.toml b/pyproject.toml index 893fdf1..5e25d4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ google-auth-httplib2 = "^0.0.3" google-auth-oauthlib = "^0.2.0" httplib2 = "^0.12.1" oauthlib = "^2.1.0" -python-magic = { version = "^0.4.15", markers = "platform_system == 'Linux'" } +python-magic = "^0.4.15" python-magic-bin = { version = "^0.4.14", markers = "platform_system == 'Windows'" } requests = "^2.18.4" requests-oauthlib = "^0.8.0" diff --git a/requirements.txt b/requirements.txt index 9ebf51f..3768210 100644 --- a/requirements.txt +++ b/requirements.txt @@ -88,7 +88,7 @@ pyasn1-modules==0.2.8 \ --hash=sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed \ --hash=sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0 \ --hash=sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd -python-magic==0.4.15; platform_system == "Linux" \ +python-magic==0.4.15 \ --hash=sha256:f3765c0f582d2dfc72c15f3b5a82aecfae9498bd29ca840d72f37d7bd38bfcd5 \ --hash=sha256:f2674dcfad52ae6c49d4803fa027809540b130db1dec928cfbb9240316831375 python-magic-bin==0.4.14; platform_system == "Windows" \