diff --git a/README.md b/README.md index 26bd1fc..547359e 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,8 @@ This containers base image is provided by: [linuxserver/lidarr](https://github.c * Downloading **Music Videos** using online sources for use in popular applications (Plex/Kodi/Emby/Jellyfin): * Completely automated * Searches Lidarr Artists (musicbrainz) video recordings for videos to download - * Saves videos in MKV format + * Saves videos in MKV format by default + * Can save videos in MP4 format for Plex metadata support * Downloads using Highest available quality for both audio and video * Saves thumbnail of video locally for Plex/Kodi/Jellyfin/Emby usage * Embed subtitles if available matching desired language @@ -121,6 +122,7 @@ Container images are configured using parameters passed at runtime (such as thos | `-e configureLidarrWithOptimalSettings=true` | true = enabled :: Automatically configures Lidarr with optimal settings | | `-e searchSort=date` | date or album :: Sorts the missing/cutoff list by release date (newest -> oldest) or album type (album -> single) for processing the list | | `-e audioFormat=native` | native or alac or mp3 or aac or opus :: native is the native download client file type, selected by the matching audio bitrate | +| `-e videoContainer=mkv` | mkv or mp4 :: Default = mkv. mp4 allows Plex to read metadata. Note mp4 videos may not be as high quality as mkv due to codec limitations of mp4. | | `-e audioBitrate=lossless` | master or lossless or high or low or ### :: master = MQA/lossless flac files, lossless = flac files, high = 320K, low = 128k/96k, ### = the output bitrate of converted lossless files to selected audioFormat that is not native, example: 192... | | `-e requireQuality=true` | true = enabled :: Downloads will be required to have the requested file format | | `-e enableReplaygainTags=true` | true = enabled :: Downloads will be tagged with Replaygain Metadata | diff --git a/arm32v7.dockerfile b/arm32v7.dockerfile index 2ce64af..6694c95 100644 --- a/arm32v7.dockerfile +++ b/arm32v7.dockerfile @@ -42,6 +42,7 @@ ENV videoFormat="bestvideo*+bestaudio/best" ENV scriptInterval=15m ENV videoDownloadTag="" ENV retryNotFound=90 +ENV videoContainer=mkv RUN \ echo "*** install packages ***" && \ diff --git a/arm64v8.dockerfile b/arm64v8.dockerfile index 1996727..5dcedb1 100644 --- a/arm64v8.dockerfile +++ b/arm64v8.dockerfile @@ -42,6 +42,7 @@ ENV videoFormat="bestvideo*+bestaudio/best" ENV scriptInterval=15m ENV videoDownloadTag="" ENV retryNotFound=90 +ENV videoContainer=mkv RUN \ echo "*** install packages ***" && \ diff --git a/root/scripts/Video.sh b/root/scripts/Video.sh index 39b4e93..e47537f 100644 --- a/root/scripts/Video.sh +++ b/root/scripts/Video.sh @@ -86,7 +86,9 @@ Configuration () { log "CONFIG :: Download Location :: $downloadPath" log "CONFIG :: Music Video Location :: $videoPath" log "CONFIG :: Subtitle Language set to: $youtubeSubtitleLanguage" - log "CONFIG :: yt-dlp format: $videoFormat" + log "CONFIG :: yt-dlp format (mkv): $videoFormat" + log "CONFIG :: yt-dlp format (mp4): --format-sort ext:mp4:m4a --merge-output-format mp4" + log "CONFIG :: Video container set to format: $videoContainer" if [ -n "$videoDownloadTag" ]; then log "CONFIG :: Video download tag set to: $videoDownloadTag" fi @@ -340,16 +342,30 @@ DownloadVideo () { fi if echo "$1" | grep -i "youtube" | read; then - if [ ! -z "$cookiesFile" ]; then - yt-dlp -f "$videoFormat" --no-video-multistreams --cookies "$cookiesFile" -o "$downloadPath/incomplete/${2}${3}" --embed-subs --sub-lang $youtubeSubtitleLanguage --merge-output-format mkv --remux-video mkv --no-mtime --geo-bypass "$1" - else - yt-dlp -f "$videoFormat" --no-video-multistreams -o "$downloadPath/incomplete/${2}${3}" --embed-subs --sub-lang $youtubeSubtitleLanguage --merge-output-format mkv --remux-video mkv --no-mtime --geo-bypass "$1" - fi - if [ -f "$downloadPath/incomplete/${2}${3}.mkv" ]; then - chmod 666 "$downloadPath/incomplete/${2}${3}.mkv" - downloadFailed=false + if [ $videoContainer = mkv ]; then + if [ ! -z "$cookiesFile" ]; then + yt-dlp -f "$videoFormat" --no-video-multistreams --cookies "$cookiesFile" -o "$downloadPath/incomplete/${2}${3}" --embed-subs --sub-lang $youtubeSubtitleLanguage --merge-output-format mkv --remux-video mkv --no-mtime --geo-bypass "$1" + else + yt-dlp -f "$videoFormat" --no-video-multistreams -o "$downloadPath/incomplete/${2}${3}" --embed-subs --sub-lang $youtubeSubtitleLanguage --merge-output-format mkv --remux-video mkv --no-mtime --geo-bypass "$1" + fi + if [ -f "$downloadPath/incomplete/${2}${3}.mkv" ]; then + chmod 666 "$downloadPath/incomplete/${2}${3}.mkv" + downloadFailed=false + else + downloadFailed=true + fi else - downloadFailed=true + if [ ! -z "$cookiesFile" ]; then + yt-dlp --format-sort ext:mp4:m4a --merge-output-format mp4 --no-video-multistreams --cookies "$cookiesFile" -o "$downloadPath/incomplete/${2}${3}" --embed-subs --sub-lang $youtubeSubtitleLanguage --no-mtime --geo-bypass "$1" + else + yt-dlp --format-sort ext:mp4:m4a --merge-output-format mp4 --no-video-multistreams -o "$downloadPath/incomplete/${2}${3}" --embed-subs --sub-lang $youtubeSubtitleLanguage --no-mtime --geo-bypass "$1" + fi + if [ -f "$downloadPath/incomplete/${2}${3}.mp4" ]; then + chmod 666 "$downloadPath/incomplete/${2}${3}.mp4" + downloadFailed=false + else + downloadFailed=true + fi fi fi @@ -394,16 +410,31 @@ VideoProcessWithSMA () { extension="${filename##*.}" filenamenoext="${filename%.*}" - if python3 /usr/local/sma/manual.py --config "/config/extended/scripts/sma.ini" -i "$file" -nt &>/dev/null; then - sleep 0.01 - log "$processCount of $lidarrArtistIdsCount :: $1 :: $lidarrArtistName :: $2 :: Processed with SMA..." - rm /usr/local/sma/config/*log* + if [[ $filenoext.$videoContainer == *.mkv ]] + then + + if python3 /usr/local/sma/manual.py --config "/config/extended/scripts/sma.ini" -i "$file" -nt &>/dev/null; then + sleep 0.01 + log "$processCount of $lidarrArtistIdsCount :: $1 :: $lidarrArtistName :: $2 :: Processed with SMA..." + rm /usr/local/sma/config/*log* + else + log "$processCount of $lidarrArtistIdsCount :: $1 :: $lidarrArtistName :: $2 :: ERROR: SMA Processing Error" + rm "$video" + log "$processCount of $lidarrArtistIdsCount :: $1 :: $lidarrArtistName :: $2 :: INFO: deleted: $filename" + fi else - log "$processCount of $lidarrArtistIdsCount :: $1 :: $lidarrArtistName :: $2 :: ERROR: SMA Processing Error" - rm "$video" - log "$processCount of $lidarrArtistIdsCount :: $1 :: $lidarrArtistName :: $2 :: INFO: deleted: $filename" + if python3 /usr/local/sma/manual.py --config "/config/extended/scripts/sma-mp4.ini" -i "$file" -nt &>/dev/null; then + sleep 0.01 + log "$processCount of $lidarrArtistIdsCount :: $1 :: $lidarrArtistName :: $2 :: Processed with SMA..." + rm /usr/local/sma/config/*log* + else + log "$processCount of $lidarrArtistIdsCount :: $1 :: $lidarrArtistName :: $2 :: ERROR: SMA Processing Error" + rm "$video" + log "$processCount of $lidarrArtistIdsCount :: $1 :: $lidarrArtistName :: $2 :: INFO: deleted: $filename" + fi fi done + } VideoTagProcess () { @@ -430,10 +461,13 @@ VideoTagProcess () { genre="" fi - mv "$filenoext.mkv" "$filenoext-temp.mkv" + if [[ $filenoext.$videoContainer == *.mkv ]] + then + + mv "$filenoext.$videoContainer" "$filenoext-temp.$videoContainer" log "$processCount of $lidarrArtistIdsCount :: $4 :: $lidarrArtistName :: ${1}${2} $3 :: Tagging file" ffmpeg -y \ - -i "$filenoext-temp.mkv" \ + -i "$filenoext-temp.$videoContainer" \ -c copy \ -metadata TITLE="${1}" \ -metadata DATE_RELEASE="$3" \ @@ -444,9 +478,33 @@ VideoTagProcess () { -metadata ALBUMARTIST="$lidarrArtistName" \ -metadata ENCODED_BY="lidarr-extended" \ -attach "$downloadPath/incomplete/${1}${2}.jpg" -metadata:s:t mimetype=image/jpeg \ - "$filenoext.mkv" &>/dev/null - rm "$filenoext-temp.mkv" - chmod 666 "$filenoext.mkv" + "$filenoext.$videoContainer" &>/dev/null + rm "$filenoext-temp.$videoContainer" + chmod 666 "$filenoext.$videoContainer" + + else + + mv "$filenoext.$videoContainer" "$filenoext-temp.$videoContainer" + log "$processCount of $lidarrArtistIdsCount :: $4 :: $lidarrArtistName :: ${1}${2} $3 :: Tagging file" + ffmpeg -y \ + -i "$filenoext-temp.$videoContainer" \ + -i "$downloadPath/incomplete/${1}${2}.jpg" \ + -map 1 \ + -map 0 \ + -c copy \ + -c:v:0 mjpeg \ + -disposition:0 attached_pic \ + -movflags faststart \ + -metadata TITLE="${1}" \ + -metadata ARTIST="$lidarrArtistName" \ + -metadata DATE="$3" \ + -metadata GENRE="$genre" \ + "$filenoext.$videoContainer" &>/dev/null + rm "$filenoext-temp.$videoContainer" + chmod 666 "$filenoext.$videoContainer" + + fi + done } @@ -748,7 +806,7 @@ for lidarrArtistId in $(echo $lidarrArtistIds); do fi fi fi - if [[ -n $(find "$videoPath/$lidarrArtistFolderNoDisambig" -maxdepth 1 -iname "${musicbrainzVideoTitleClean}${plexVideoType}.mkv") ]]; then + if [[ -n $(find "$videoPath/$lidarrArtistFolderNoDisambig" -maxdepth 1 -iname "${musicbrainzVideoTitleClean}${plexVideoType}.mkv") ]] || [[ -n $(find "$videoPath/$lidarrArtistFolderNoDisambig" -maxdepth 1 -iname "${musicbrainzVideoTitleClean}${plexVideoType}.mp4") ]]; then log "$processCount of $lidarrArtistIdsCount :: MBZDB :: $lidarrArtistName :: ${musicbrainzVideoTitle}${musicbrainzVideoDisambiguation} :: Previously Downloaded, skipping..." continue fi @@ -838,7 +896,7 @@ for lidarrArtistId in $(echo $lidarrArtistIds); do tidy -w 2000 -i -m -xml "$videoPath/$lidarrArtistFolderNoDisambig/${videoTitleClean}${plexVideoType}.nfo" &>/dev/null fi fi - if [[ -n $(find "$videoPath/$lidarrArtistFolderNoDisambig" -maxdepth 1 -iname "${videoTitleClean}${plexVideoType}.mkv") ]]; then + if [[ -n $(find "$videoPath/$lidarrArtistFolderNoDisambig" -maxdepth 1 -iname "${videoTitleClean}${plexVideoType}.mkv") ]] || [[ -n $(find "$videoPath/$lidarrArtistFolderNoDisambig" -maxdepth 1 -iname "${videoTitleClean}${plexVideoType}.mp4") ]]; then log "$processCount of $lidarrArtistIdsCount :: IMVDB :: $lidarrArtistName :: ${imvdbVideoTitle} :: Previously Downloaded, skipping..." continue fi diff --git a/root/scripts/sma-mp4.ini b/root/scripts/sma-mp4.ini new file mode 100644 index 0000000..1faff5b --- /dev/null +++ b/root/scripts/sma-mp4.ini @@ -0,0 +1,267 @@ +[Converter] +ffmpeg = ffmpeg +ffprobe = ffprobe +threads = 0 +hwaccels = +hwaccel-decoders = +hwdevices = +hwaccel-output-format = +output-directory = +output-directory-space-ratio = 0.0 +output-format = mp4 +output-extension = mp4 +temp-extension = +minimum-size = 0 +ignored-extensions = nfo, ds_store +copy-to = +move-to = +delete-original = True +process-same-extensions = True +bypass-if-copying-all = False +force-convert = True +post-process = False +wait-post-process = False +detailed-progress = False +opts-separator = , +preopts = +postopts = +regex-directory-replace = [^\w\-_\. ] + +[Permissions] +chmod = 0666 +uid = -1 +gid = -1 + +[Metadata] +relocate-moov = True +full-path-guess = True +tag = True +tag-language = eng +download-artwork = poster +sanitize-disposition = +strip-metadata = True +keep-titles = False + +[Video] +codec = copy +max-bitrate = 0 +bitrate-ratio = +crf = -1 +crf-profiles = +preset = +codec-parameters = +dynamic-parameters = False +max-width = 0 +profile = +max-level = 0.0 +pix-fmt = +prioritize-source-pix-fmt = True +filter = +force-filter = False + +[HDR] +codec = +pix-fmt = +space = bt2020nc +transfer = smpte2084 +primaries = bt2020 +preset = +codec-parameters = +filter = +force-filter = False +profile = + +[Audio] +codec = copy +languages = +default-language = +include-original-language = True +first-stream-of-language = False +channel-bitrate = 128 +variable-bitrate = 0 +max-bitrate = 0 +max-channels = 0 +filter = +profile = +force-filter = False +sample-rates = +sample-format = +copy-original = False +aac-adtstoasc = False +ignored-dispositions = +force-default = False +unique-dispositions = False +stream-codec-combinations = + +[Audio.Sorting] +sorting = language, channels.d, map, d.comment +default-sorting = channels.d, map, d.comment +codecs = + +[Universal Audio] +codec = +channel-bitrate = 128 +variable-bitrate = 0 +first-stream-only = False +filter = +profile = +force-filter = False + +[Audio.ChannelFilters] +6-2 = pan=stereo|FL=0.5*FC+0.707*FL+0.707*BL+0.5*LFE|FR=0.5*FC+0.707*FR+0.707*BR+0.5*LFE + +[Subtitle] +codec = srt +codec-image-based = copy +languages = +default-language = +first-stream-of-language = False +encoding = +burn-subtitles = False +burn-dispositions = +embed-subs = True +embed-image-subs = True +embed-only-internal-subs = True +filename-dispositions = forced +ignore-embedded-subs = False +ignored-dispositions = +force-default = False +unique-dispositions = True +attachment-codec = +remove-bitstream-subs = False +include-original-language = False + +[Subtitle.Sorting] +sorting = language, d.comment, d.default.d, d.forced.d +burn-sorting = language, d.comment, d.default.d, d.forced.d +codecs = + +[Subtitle.CleanIt] +enabled = False +config-path = +tags = + +[Subtitle.Subliminal] +download-subs = False +download-hearing-impaired-subs = False +providers = +download-forced-subs = False +include-hearing-impaired-subs = False + +[Subtitle.Subliminal.Auth] +opensubtitles = +tvsubtitles = + +[Sonarr] +host = localhost +port = 8989 +apikey = +ssl = False +webroot = +force-rename = False +rescan = True +block-reprocess = False + +[Radarr] +host = localhost +port = 7878 +apikey = +ssl = False +webroot = +force-rename = False +rescan = True +block-reprocess = False + +[Sickbeard] +host = localhost +port = 8081 +ssl = False +apikey = +webroot = +username = +password = + +[Sickrage] +host = localhost +port = 8081 +ssl = False +apikey = +webroot = +username = +password = + +[SABNZBD] +convert = True +sickbeard-category = sickbeard +sickrage-category = sickrage +sonarr-category = sonarr +radarr-category = radarr +bypass-category = bypass +output-directory = +path-mapping = + +[Deluge] +sickbeard-label = sickbeard +sickrage-label = sickrage +sonarr-label = sonarr +radarr-label = radarr +bypass-label = bypass +convert = True +host = localhost +port = 58846 +username = +password = +output-directory = +remove = False +path-mapping = + +[qBittorrent] +sickbeard-label = sickbeard +sickrage-label = sickrage +sonarr-label = sonarr +radarr-label = radarr +bypass-label = bypass +convert = True +action-before = +action-after = +host = localhost +port = 8080 +ssl = False +username = +password = +output-directory = +path-mapping = + +[uTorrent] +sickbeard-label = sickbeard +sickrage-label = sickrage +sonarr-label = sonarr +radarr-label = radarr +bypass-label = bypass +convert = True +webui = False +action-before = +action-after = +host = localhost +ssl = False +port = 8080 +username = +password = +output-directory = +path-mapping = + +[Plex] +host = localhost +port = 32400 +refresh = False +token = +username = +password = +servername = +ssl = True +ignore-certs = False +path-mapping = + +[Subtitle.FFSubsync] +enabled = False +