Giter VIP home page Giter VIP logo

mkvbatchmultiplex's Introduction

mkvbatchmultiplex: MKVMERGE batch multiplexing

image

Changes since version 2.0.0

  • The applications has been ported to Python 3.12 and PySide6.
  • Added option to compute the CRC-32 of the output files and add it to the end of the file name.
  • MKVToolnix is now embedded to help with the Linux .version
  • MediaInfo is now also embedded. Pymediainfo was providing the library for Windows in the more recent versions of the package.

Description

mkvbatchmultiplex program is for processing mkvmerge command line and use it as a template to apply the multiplex instructions to all the files found in the directory. The command line is expected to be taken from mkvtoolnix-gui:

Multiplexer->Show command line

mkvmerge and mkvtoolnix-gui are part of the MKVToolNix set of tools to work with Matroska media container files.

Works with Windows (cmd.exe) or Linux/unix shells (bash, zsh, etc.)

Installation

pip install mkvbatchmultiplex

It is been developed on Windows the media server is Windows based. Made light testing on ubuntu. Will try to do more testing in linux ubuntu at least. Many reports of not woriking on linux are solved by installing MediaInfo this is an external dependency.

macOS will no longer be supported don't have harware to make tests.

If working with the source to execute the application first create the locale files. On the source directory execute:

python setup.py generate_catalog

Dependencies

Usage

It assumed you have working knowledge of using MKVToolNix. Select a file make any operations needed copy command to clipboard. Remember to select and output directory:

Multiplexer->Show command line

Paste command on mkvbatchmultiplex push <Add Queue> is there are no more jobs push <Start Worker> button and wait.

Step by step examples are in the github repository wiki.

Algorithms explained

When the worker is processing a job before starting to work on a set of files the structure will be checked. If the structure is the same as in the pasted command the files are processed. If it doesn't match the program will behave according to the algorithm selected.

New algorithms:

With all Algorithms any file that is not flagged with and invalid structure the results are the same. They are different when the files are flagged with and invalid structure on what they do.

  1. Algorithm 0 current behavior. If the structure check fails no command will be executed files have to be logically equal. The resulting file will have the same structure as the destination file on the command line. The resulting file is very likely to be the expected result as specified on the command line. If no file is flagged random checks usually are sufficient. Any flagged file has to be check to fix any problem and maybe run the command with MKVToolNix for that file.
  2. Algorithm 1 if structure check fails it will try to find the tracks that best matches the base file and adjust the command accordingly. Any track not used in the command will be ignored. If no suitable track found no command will not execute. Resulting file structure if the same as in the command line but is not as likely to be the desired file as in Algorithm 0. Flagged files should be checked to see if the file is ok.
  3. Algorithm 2 if Algorithm 1 fails tracks without match will be ignored and and the command still will execute. The resulting file will not be like the destination file in the original command. It may even be unusable. Any flagged has to be check to see if is usable.

Since in some occasions Algorithm 1 will produce the correct file it will be set as the default. The original files should never be erased until all the new files are watched or at least check with a player that the all the tracks are muxed as needed.

One case in which Algorithm 2 applies is when some episodes have commentary audio tracks. Algorithm 1 will fail because on files with missing commentary tracks there will not be enough audio tracks to produce a file with a structure logically equal. There are more tracks needed than tracks available. Algorithm 2 will ignore this and proceed. Additional files with commentary tracks will be muxed with it.

Other important difference to the current behavior is that text and audio tracks are match by language not format. For example:

In the original source the audio is English with flac format and in the current file is English with ac3. This track will be used for muxing the files.

For video tracks the language is always ignored in the testing.

Personally I still used Algorithm 0 because if I close the program without checking the flagged files just looking in the directory any missing file will exactly correspond to a flagged file. For more meticulous users Algorithm 1 is the better one.

Roadmap

This is just the base for the project. The roadmap is:

  • Work on rename module to fetch metadata
  • Work on the Spanish interface translation
  • Easier installation for linux

For linux installations the AppImage binary format will be used for the forseeable future.

If the the program generates any interest any further changes and additions will also depend on user base needs.

mkvbatchmultiplex's People

Contributors

akai10tsuki avatar dependabot[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

mkvbatchmultiplex's Issues

[Bug/Performance] Performance Drop

I was hoping not ... Apparently the new file detection system is a bit too slow. For example:
With previous versions, I entered the command for a batch of 25 ep with a total of 55gb and its corresponding HEVC, always 25 ep, (used to decrease the total size).
In 3/5 seconds came the message that the command was OK. At that point I checked and in a short time (no more than 2-3 minutes) the check finished.

Now, just to paste the command and wait for the OK from the program, it also takes 10 15 minutes.
I'm not talking about the check ... (for a batch like the one described above I left the pc all night and in the morning it was still doing the last 2 episodes).

If speed couldn't be improved, wouldn't it be possible to reintroduce the old system together with this new one? So you can choose (after all the BOM problem is mostly about files NOT already inside MKV).
I didn't write this issue right away because I wanted to understand if it was due to these 50gb batches (2 - 3gb for single file) or if it was in general. In the end it is both for the file size and in general.
Thanks in advance

OS: Windows 10 x64 (on SSD 500gb Kingstone)
10 GB Ram
I5-4460
HDD Sata 3 2.5" (internal)

Edit1:

Mainly on files of this type:
MKV
VIDEO: x264 (from 1.5 to 3gb)
AUDIO1: FLAC 2.0
AUDIO2: FLAC 5.1
AUDIO3 DTS-HD_MA 5.1
Sub1: ASS
Sub2: ASS
Sub3: HDMV PGS
Sub4: HDMV PGS
Chapter

Example this torrent: https://nyaa.si/view/1256326

[BUG? / Feature] --track-order for MKS/A (MKV without Video or/and Audio) export a .mkv instead of an MKS / A

As the title suggests, I went into batching to export to MKA / S.
And apparently it works. Except that it exports to .MKV (thankfully MKV toolnix sees it anyway) I know that BulkRename would be enough to change .mkv to the right one.

@moralia

The system to make the batch work (which I finally understood):

C:\Users\MSI\Downloads\EXE\mkvtoolnix\mkvmerge.exe --ui-language it --output ^"G:\Backup Anime\[KonaWolf Fansub] Miru Tights - 01v2 [BD].mks^" --no-audio --no-video --sub-charset 2:UTF-8 --language 2:it --track-name 2:KonaWolf --default-track 2:yes ^"^(^" ^"C:\Users\MSI\Downloads\Telegram Desktop\[KonaWolf Fansub] Miru Tights [BD]\[KonaWolf Fansub] Miru Tights - 01v2 [BD].mkv^" ^"^)^" --title ^"^"
is the original command from MKVToolNix GUI
image
Just add --track-order 0:2 to the end of the command.
0:2 must be replaced with track order (in my case for MKVToolNix it is seen as 0: 2, obviously if you move the tracks, the values also change).

Problem:

Export to .mkv


Anyway thanks to this I can now batch 800 episodes to extract audio / subs only ๐Ÿ‘๐Ÿ˜๐Ÿ‘Œ๐Ÿ˜ต๐Ÿฑโ€๐Ÿ

[BUG] Deleted queues are still performed

image
Jobs in this list even if they are deleted, when you start the worker they are executed.

How to reproduce error:

Add a Job,
Go to the Job list and delete it,
add another job,
start the worker...
= The job you deleted starts.

I actually encountered this problem in previous versions as well.
Thanks in advance (as you can see I've done nearly 200 jobs by now๐Ÿ˜)

[BUG] Again! - Does nothing with some mks (for no specific reason?)

Paths:
Z:\Anime Backups\Nisekoi\S02\XIII's Fansub\[XIII's Fansub] Nisekoi S2 01 Sub ITA - Da ora in poi_Notalo.mks (Network path), but it also happens on any folder of a HDD / SSD.
I:\Tor\[Rakuen Subs] Nisekoi S2\[Rakuen Subs]Nisekoi S2 - 01 [SubITA - Hi10P] [09F2BE90].mkv

Logs:
MKVBatchMultiplex-Logs.zip
FileNames.txt

Files:
XIII's Fansub (MKS).zip
As soon as the up on mega is finished, I also update for the ep. https://mega.nz/file/YhNXFDTY#7o22bFCj58O7PW6iqhLZFFtcCOv1KT8jdTjACvdrSfk

Com:

C:\Users\MSI\Downloads\EXE\mkvtoolnix\mkvmerge.exe --ui-language it --output ^"I:\ttt\[Rakuen Subs]Nisekoi S2 - 01 [SubITA - Hi10P] [09F2BE90].mkv^" --no-chapters --language 0:ja --track-name ^"0:[Rakuen Subs] x264 10bit Web ^(commie^)^" --default-track 0:yes --display-dimensions 0:1280x720 --language 1:ja --track-name ^"1:AAC 2.0 Jpn^" --default-track 1:yes --sub-charset 2:UTF-8 --language 2:it --track-name ^"2:Rakuen Subs ^(Web^)^" --default-track 2:yes ^"^(^" ^"I:\Tor\[Rakuen Subs] Nisekoi S2\[Rakuen Subs]Nisekoi S2 - 01 [SubITA - Hi10P] [09F2BE90].mkv^" ^"^)^" --sub-charset 0:UTF-8 --language 0:it --track-name ^"0:XIII's Fansub ^(Web^)^" ^"^(^" ^"Z:\Anime Backups\Nisekoi\S02\XIII's Fansub\[XIII's Fansub] Nisekoi S2 01 Sub ITA - Da ora in poi_Notalo.mks^" ^"^)^" --title ^"[Rakuen Subs]Nisekoi S2 - 01 - D'Ora In Poi ^& Ti Prego, Notami^" --track-order 0:0,0:1,0:2,1:0

Remarks:
Eventually I did the batch individually and MkvToolNix pointed out to me that of the MKS 4 and 5 have a character set problem (it continued anyway). It could be the one that blocked the software.

[BUG] No response from the program (cause unknown)

I have been experiencing an anomalous problem with the program for some time now.
Sometimes, copying the command to the program returns that the command is ok but both adding to the queue (and starting it) and checking the consistency (or any other option) DO NOT give results, as if I had never pressed the key.

Windows 10 64bit 20H2;
MkvToolnixGui: 5.1.0.0
MkvBatchMultiplex: 2.1.0b1.dev2
Python: 3.8.6 AMD64

Ex Command:
C:\Users\MSI\Downloads\EXE\mkvtoolnix\mkvmerge.exe --ui-language it --output ^"Y:\Anime2\Isekai Cheat Magician\Stagione 01\[KH] Isekai Cheat Magician - 01 - Lost Ones from Another World.mkv^" --language 0:ja --track-name ^"0:[KH] x264 10bit BD^" --default-track 0:yes --display-dimensions 0:1920x1080 --language 1:ja --track-name ^"1:FLAC 2.0 Jpn^" ^"^(^" ^"G:\_torrent\_HEVC-Conv\[KH] Isekai Cheat Magician ^(BD 1080p^) [Dual-Audio]\Remux\[KH] Isekai Cheat Magician - 01 - Lost Ones from Another World.mkv^" ^"^)^" --language 0:it --track-name ^"0:Crunchyroll Ita^" --sync 0:1024 ^"^(^" ^"Z:\Anime Backups\Isekai Cheat Magician\[Erai-raws] Isekai Cheat Magician - 01.ass^" ^"^)^" --attachment-name arial.ttf --attachment-mime-type application/x-truetype-font --attach-file ^"Z:\Anime Backups\Isekai Cheat Magician\font\arial.ttf^" --attachment-name times.ttf --attachment-mime-type application/x-truetype-font --attach-file ^"Z:\Anime Backups\Isekai Cheat Magician\font\times.ttf^" --attachment-name trebuc.ttf --attachment-mime-type application/x-truetype-font --attach-file ^"Z:\Anime Backups\Isekai Cheat Magician\font\trebuc.ttf^" --attachment-name trebucit.ttf --attachment-mime-type application/x-truetype-font --attach-file ^"Z:\Anime Backups\Isekai Cheat Magician\font\trebucit.ttf^" --track-order 0:0,0:1,1:0

IMG's

image
image
start worker result:
image
No job errors, Job set as done (but nothing done) and Log view:
image
The command works on MkvToolNix Gui (so it is not wrong and / or corrupt some file).

In this case, if I run the command by removing the .ass from the mux then it works (HOWEVER IT'S NOT ALWAYS LINKED TO .ass, sometimes it happens with single MKVs, so I think the problem is related but elsewhere at the same time).

Log:
MKVBatchMultiplex.log

Used Files:
https://mega.nz/folder/xsNBXAAY#JQmLV33OOpt4_j9uHlYPGQ

or original Downloaded sources:
Subs: https://txt.erai-raws.info/?dir=Sub/2019/Summer/Isekai%20Cheat%20Magician/01%20~%2012/Italian
MKV: https://nyaa.si/view/1208878

In this case in the command you will notice that the files are not identical to the originals, I thought the problem was in the names (and instead even renaming and changing the file structure the problem persists)

[Bug and Question] Merge elements of two ".mkv" into one ".mkv" on windows with network folders.

I don't understand, both V1 and V2 don't say why. Basically I have to batch a 64 episode series, I have to change the original video track with the hevc version and delay it for 1000ms. I do all the necessary things and copy the command from the shell. I paste the command and do analyze, it says OK (Structure looks OK :).
image
I click on process and says ok for all 64 in type 20 total seconds. No new files are created and nothing happens.

C:\Users\MSI\Downloads\EXE\mkvtoolnix\mkvmerge.exe --ui-language it --output ^"Y:\Anime2\Fullmetal Alchemist Brotherhood\Stagione 01\Fullmetal Alchemist Brotherhood - s01e64 - stress - La fine del viaggio1.mkv^" --audio-tracks 1 --no-video --subtitle-tracks 3 --no-chapters --language 1:ita --track-name ^"1:Italiano AC3 5.1^" --default-track 1:yes --language 3:ita --track-name 3:Cartelli --default-track 3:yes ^"^(^" ^"Y:\Anime2\Fullmetal Alchemist Brotherhood\Stagione 01\Fullmetal Alchemist Brotherhood - s01e63 - stress - L'altro lato del cancello.mkv^" ^"^)^" --language 0:jpn --display-dimensions 0:1916x1076 --language 1:jpn --track-name ^"1:Giapponese AC3 2.0^" --language 2:ita --track-name 2:Dialoghi --language 3:eng --track-name 3:Dialoghi ^"^(^" ^"Y:\Anime2\Fullmetal Alchemist Brotherhood\Stagione 01\Fullmetal Alchemist Brotherhood - s01e64 - stress - La fine del viaggio.mkv^" ^"^)^" --track-order 1:0,0:1,1:1,0:3,1:2,1:3

Found the problem but I don't know why it happens: C:\Users\MSI\Downloads\EXE\mkvtoolnix\mkvmerge.exe
if put as shell, when you do output command (the one to see what it would do) it doesn't put " \ " in the path (only for the path of mkvtoolnix). Fix: "" at the beginning and end. It happens with both V1 and V2.

Abort Job buttons

When you do an "abort job" (also the other one, the one next to it), it aborts ... yes ... but it also causes the subsequent batches to go into abort (those you add + those already present in the list ).
Both keys cause any job to abort. You must then close and reopen the program to continue using it.

109984264-e24cf400-7d03-11eb-977c-f23aa0a6680d

[BUG] Error source files total mismatched

Almost 90% of the times I enter the command I get this error:

Command looks ok. Command looks ok. Analysis of command line: chk: Command seems ok. chk: mkvmerge ok - C:\Users\MSI\Downloads\EXE\mkvtoolnix\mkvmerge.exe. chk: Destination directory ok = Y:\Anime2\Boruto - Naruto Next Generations\Episodes 001-016 (The Ghost Incident). chk: Source directory ok - Y:\Anime2\Boruto - Naruto Next Generations\Episodes 001-016 (The Ghost Incident). chk: Source directory ok - Z:\Anime Backups\Boruto\Erai-raws\Italian_1-77. err: Error source files total mismatched.--language 0:ita --track-name '0:Crunchyroll Ita' --default-track 0:yes --sync 0:26 '(' 'Z:\Anime Backups\Boruto\Erai-raws\Italian_1-77\[Erai-raws] Boruto - Naruto Next Generations - 01 [720p][Multiple Subtitle]_track8_ita.ass' ')'

The command is this:

C:\Users\MSI\Downloads\EXE\mkvtoolnix\mkvmerge.exe --ui-language it --output ^"Y:\Anime2\Boruto - Naruto Next Generations\Episodes 001-016 ^(The Ghost Incident^)\Boruto - s01e01 - AnimeRG - Uzumaki Boruto^!^! ^(1^).mkv^" --language 0:jpn --track-name ^"0:[AnimeRG]^" --default-track 0:yes --display-dimensions 0:1920x1080 --language 1:jpn --track-name ^"1:Japanese Stereo^" --default-track 1:yes --sub-charset 2:UTF-8 --language 2:eng --track-name ^"2:Crunchyroll Eng^" --sub-charset 3:UTF-8 --language 3:ita --track-name ^"3:Crunchyroll Ita SRT^" ^"^(^" ^"Y:\Anime2\Boruto - Naruto Next Generations\Episodes 001-016 ^(The Ghost Incident^)\Boruto - s01e01 - AnimeRG - Uzumaki Boruto^!^!.mkv^" ^"^)^" --language 0:ita --track-name ^"0:Crunchyroll Ita^" --default-track 0:yes --sync 0:26 ^"^(^" ^"Z:\Anime Backups\Boruto\Erai-raws\Italian_1-77\[Erai-raws] Boruto - Naruto Next Generations - 01 [720p][Multiple Subtitle]_track8_ita.ass^" ^"^)^" --track-order 0:0,0:1,0:2,0:3,1:0

Almost everything I try to automate with this tool gives me this error.

image
image

Failed to execute script RunApp

I am also having the same issue. (Sorry for bad English)
Version 2.1

Default Location (C:/Program Files/)
So, what I did was that i wanted to cancel a job/task so to cancel it, I go the task manager and just "End Task" the task, this was the first time i did this to cancel it. After few seconds i have the Windows blue Screen and then my computer restart. After computer restart i try opening the software but shows "Failed to execute script RunApp". So, i download the lastest version with a clean install but same error. Same with portable version and older versions. Now i can't run the software no matter with version. Could solve this problem please?

RegEX bug?

image
I'm not very familiar with the world of RegEX, But for some reason, even the content after "-" is seen as part of the "Name" group.
image

Original File name: The Irregular at Magic High School - S01E01.mkv
MKVToolNix edits: The Irregular at Magic High School - S01E01.mks

RegEX: ([([].*?[])]\W*|)(?P<name>.*?)(\W*-\W*|\W*-|\W*|)(?P<episode>\d+).*
Subs: The Irregular at Magic High School - S01E\g<episode> - Owari (Kagome edit) -

WebSite used for Check: https://regex101.com/

I forgot to put the original version:

image

Linux | Launch error

When I am trying to launch the multiplex error occurs.
image
How can I fix it?
I am using debian 11 . And python 3.9.2
Thanks a lot

Linux Appimage OSError Mediainfo not found.

Everything is installed. Program starts but starting workers doesn't start a process.. Readthedocs says mediainfo path has to be in path. Well they don't provide what the path variable should be though. But my mediainfo executable is in /usr/bin/mediainfo. /usr/bin is in my path obviously. Actually no version of this program works for me. The git version won't even install even with different python versions from pyenv... Installing via pip in python 3.8.1 clicking start workers doesn't even do anything.

File titles are removed

I noticed the program removes titles even if they were properly set in mkvtoolnix.

Example:
This is the command gotten from mkvtoolnix.

"C:\Program Files\MKVBatchMultiplex_internal\embed\mkvtoolnix\mkvmerge.exe" --ui-language en --priority lower --output ^"C:\Users\PC1\Desktop\Tsuki ga Michibiku Isekai Douchuu - S2 EP11.mkv^" --attachments 2,3,4,5,6,7,8,9,10,11,12,13,14,15 --language 0:und --track-name 0: --display-dimensions 0:1920x1080 --language 1:ja --sub-charset 2:UTF-8 --language 2:en ^"^(^" ^"C:\Users\PC1\Desktop\Tsuki ga Michibiku Isekai Douchuu 2nd Season\Tsuki ga Michibiku Isekai Douchuu - S2 EP11.mkv^" ^"^)^" --title ghjjhgjh --track-order 0:0,0:1,0:2

Title was changed to "ghjjhgjh" on this particular episode, Episode 11

This is the command created for Episdoe 11. Title has been reset to the default value

_['C:/Program Files/MKVBatchMultiplex/internal/embed/mkvtoolnix/mkvmerge.exe', '--ui-language', 'en', '--priority', 'lower', '--output', 'C:\Users\PC1\Desktop\Tsuki ga Michibiku Isekai Douchuu - S2 EP11.mkv', '--attachments', '2,3,4,5,6,7,8,9,10,11,12,13,14,15', '--language', '0:und', '--track-name', '0:', '--display-dimensions', '0:1920x1080', '--language', '1:ja', '--sub-charset', '2:UTF-8', '--language', '2:en', 'C:\Users\PC1\Desktop\Tsuki ga Michibiku Isekai Douchuu 2nd Season\Tsuki ga Michibiku Isekai Douchuu - S2 EP11.mkv', '--title', 'S02E11-Summer of Growth and New Skills', '--track-order', '0:0,0:1,0:2']

These are the commands created for other episodes in the directory. Title section is empty

_['C:/Program Files/MKVBatchMultiplex/internal/embed/mkvtoolnix/mkvmerge.exe', '--ui-language', 'en', '--priority', 'lower', '--output', 'C:\Users\PC1\Desktop\Tsuki ga Michibiku Isekai Douchuu - S2 EP12.mkv', '--attachments', '2,3,4,5,6,7,8,9,10,11,12,13,14,15', '--language', '0:und', '--track-name', '0:', '--display-dimensions', '0:1920x1080', '--language', '1:ja', '--sub-charset', '2:UTF-8', '--language', '2:en', 'C:\Users\PC1\Desktop\Tsuki ga Michibiku Isekai Douchuu 2nd Season\Tsuki ga Michibiku Isekai Douchuu - S2 EP12.mkv', '--title', '', '--track-order', '0:0,0:1,0:2']

_['C:/Program Files/MKVBatchMultiplex/internal/embed/mkvtoolnix/mkvmerge.exe', '--ui-language', 'en', '--priority', 'lower', '--output', 'C:\Users\PC1\Desktop\Tsuki ga Michibiku Isekai Douchuu - S2 EP13.mkv', '--attachments', '2,3,4,5,6,7,8,9,10,11,12,13,14,15', '--language', '0:und', '--track-name', '0:', '--display-dimensions', '0:1920x1080', '--language', '1:ja', '--sub-charset', '2:UTF-8', '--language', '2:en', 'C:\Users\PC1\Desktop\Tsuki ga Michibiku Isekai Douchuu 2nd Season\Tsuki ga Michibiku Isekai Douchuu - S2 EP13.mkv', '--title', '', '--track-order', '0:0,0:1,0:2']

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.