Skip to content

Commit

Permalink
Add .VSMETA file encoder support (beta version)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lqlsoftware committed Aug 6, 2022
1 parent 353d9df commit 8c01641
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 4 deletions.
1 change: 1 addition & 0 deletions avutil/encoder/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from avutil.encoder.nfo import NFOEncoder
from avutil.encoder.vsmeta import VSMETAEncoder
124 changes: 124 additions & 0 deletions avutil/encoder/vsmeta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import base64
import hashlib
import time
import bitstring


def int2bytes(integer: int):
''' Encode integer as bytes with vsmeta format
eg: 128 => b'0x8001'
127 => b'0x7f'
'''
bs = bitstring.BitStream()
while integer >= 128:
# Mark highest digit of uint8 with 0x80
bs.append(((integer % 128) | 0x80).to_bytes(1, 'little'))
integer = integer >> 7
bs.append(integer.to_bytes(1, 'little'))
return bs.bytes


def str2bytes(string: str):
''' Encode string as bytes with vsmeta format
bytes: [toBytes(len), string]
'''
bs = bitstring.BitStream()
bs.append(int2bytes(len(string.encode("utf-8"))))
bs.append(string.encode("utf-8"))
return bs.bytes


class VSMETAEncoder:

ext = ".vsmeta"

def __init__(self):
pass

def encode(self, video: dict):
''' encode video info as vsmeta format
return bytes that encode using vsmeta format
VSMETA is appliable in Synology Video Station
'''
bs = bitstring.BitStream()

# Header
bs.append(b"\x08\x01")

# Main Title
bs.append(b"\x12")
bs.append(str2bytes(video.get("designatio")))
bs.append(b"\x1a")
bs.append(str2bytes(video.get("designatio")))

# Title
bs.append(b"\x22")
bs.append(str2bytes(video.get("title")))

# Date
bs.append(b"\x28\xe6\x0f\x32")
bs.append(str2bytes(video.get("date")))

# Locked (not update from internet)
bs.append(b"\x38\x01")

# Summary
if (len(video.get("outline")) > 0):
bs.append(b"\x42")
bs.append(str2bytes(video.get("outline")))

# Meta json (null)
bs.append(b"\x4a\x04\x6e\x75\x6c\x6c")

# Group Info
info = bitstring.BitStream()
# Cast
for actor in video.get("cast"):
info.append(b"\x0a")
info.append(str2bytes(actor))
# Director
info.append(b"\x12")
info.append(str2bytes(video.get("director")))
# Genre
for genre in video.get("genres"):
info.append(b"\x1a")
info.append(str2bytes(genre))
# End of Group
bs.append(b"\x52")
bs.append(int2bytes(len(info)))
bs.append(info)

# Classification
bs.append(b"\x5a")
bs.append(str2bytes("R18+"))

# Poster (BASE64 + MD5)
bs.append(b"\x8a\x01")
poster_b64_bytes = base64.b64encode(video.get('poster'))
bs.append(int2bytes(len(poster_b64_bytes)))
bs.append(poster_b64_bytes)
bs.append(b"\x92\x01")
bs.append(str2bytes(hashlib.md5(poster_b64_bytes).hexdigest()))

# Group Info
info = bitstring.BitStream()
# Background (BASE64 + MD5)
info.append(b"\x0a")
background_b64_bytes = base64.b64encode(video.get('fanart'))
info.append(int2bytes(len(background_b64_bytes)))
info.append(background_b64_bytes)
info.append(b"\x12")
info.append(str2bytes(hashlib.md5(background_b64_bytes).hexdigest()))
# Timestamp
info.append(b"\x18")
info.append(int2bytes(int(time.time() // 1000)))
# End of Group
bs.append(b"\xaa\x01")
bs.append(int2bytes(len(info)))
bs.append(info)

return bs.bytes
6 changes: 6 additions & 0 deletions avutil/tidy_up.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ def get_arguments(args=sys.argv[1:]):
help="data source of video info, 'library' or 'bus'")
parser.add_argument("-t", "--thread", dest='thread', type=int,
help="threads num, use multi-threads to download info & images")
parser.add_argument("-e", "--encoder", dest='encoder',
help="encoder of meta-data, 'nfo'(default) or 'vsmeta'")
parser.add_argument("--with-poster", dest='with_poster', action='store_true',
help="save poster")
return parser.parse_args(args)
Expand Down Expand Up @@ -97,6 +99,10 @@ def main():
if args.source == "bus":
source = avutil.source.Bus

# Encoder
if args.encoder == "vsmeta":
encoder = avutil.encoder.VSMETAEncoder

# Gen poster
if args.with_poster == True:
with_poster = True
Expand Down
4 changes: 1 addition & 3 deletions avutil/util.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import os
import re

from avutil.video import Video


def Extract_designatio(file_name):
''' Extract designatio from given name (string)
Expand Down Expand Up @@ -42,7 +40,7 @@ def Search_folder(folder, media_suffix={".mp4", ".wmv", ".avi", ".mkv"}, recursi
continue

# info already saved
if os.path.exists(os.path.join(folder, file_name[0] + ".nfo")):
if os.path.exists(os.path.join(folder, file_name[0] + ".nfo")) or os.path.exists(f + ".vsmeta"):
continue

# extract
Expand Down
8 changes: 7 additions & 1 deletion avutil/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from avutil.info import VideoInfo
# from avutil import VideoInfo
from avutil.encoder import NFOEncoder
from avutil.encoder import VSMETAEncoder
from avutil.source import Library
from avutil.source import Bus

Expand Down Expand Up @@ -222,7 +223,12 @@ def save_info(self, dst_dir=None, encoder=NFOEncoder):
# Join path (slices)
dsts = []
for idx in range(self.slices):
dsts.append(os.path.join(dst_dir, self.dst_path[idx] + ".nfo"))
if encoder == VSMETAEncoder:
dsts.append(os.path.join(
dst_dir, self.dst_path[idx] + self.file_path[idx].ext + encoder.ext))
else:
dsts.append(os.path.join(
dst_dir, self.dst_path[idx] + encoder.ext))

# Already exist or dst dir not exist
if not os.path.exists(dst_dir):
Expand Down

0 comments on commit 8c01641

Please sign in to comment.