borewit / listfix Goto Github PK
View Code? Open in Web Editor NEWlistFix() - Playlist Repair Done Right
License: GNU General Public License v3.0
listFix() - Playlist Repair Done Right
License: GNU General Public License v3.0
Please add the F2 keyboard shortcut for quick access wherever renaming is required, for e.g. playlists and files.
Work-flow and productivity are sure to gain from its addition.
Location — Playlist Directory:
The rename action for root-folders and subfolders — currently available from their context menu — is inaccessible (greyed out).
Please add it back to support renaming these folders.
How do I run on Linux?
"Linux users can smbmount a network drive and then use the mount point as a media directory " this means nothing to me
also what happened to the.jar version of this program?
@Borewit: Re listFix() v2.4.2:
I am not at all sure about this, but selecting "Find Exact Matches" to fix a broken list appears to not deliver what resembles a result, or flag a clue of some sort. It does briefly display the progress window, though. Screen video below.
In addition, do I post any questions etc, I may have about the app in general, here under issues, or perhaps elsewhere?
Thank you.
The dual progress bar shows progress of execution one or more background tasks (sub-tasks).
The total progress is the progress of the tasks, where the second progress bar shows the progress of the current background tasks being executed.
Current behavior is that the total progress-bar counts the sub-task as completed before it's even started.
If there is sub-single task to be completed , it immediately jumps to completed.
Expected behavior
The progress of the sub-tasks, steers relative progress of total.
So if there are 4 sub-tasks to perform.
So the total progress is calculated as:
total-progress = subtasks completed * (100% / number-of-subtasks) + progress-current-subtask * (100% / number-of-subtasks)
We now have support for dragging and dropping playlist folders directly into the PLD — as from listFix_2.6.0-PR113-5 .
🛠️ Please, provide support also for dragging and dropping selected Playlist(s) from their location to the PLD.
If this option is infeasible, could the support move refocus to open single, or multiple, selected playlists into the Playlist Editor, direct from their location in the operating system explorer?
The Gradle task to build the executable seems to fail: gradlew createExe
Execution failed for task ':createExe'.
> Could not initialize class net.sf.launch4j.config.ConfigPersister
When using the Save As to save a copy of a playlist under a different name:
Related to #79
In Windows listFix() .m3u8
playlists with a BOM field. On Linux it does not do that.
Although there is no clear specification for .m3u8
, I doubt that the idea of the playlist is that they have an OS dependent flavor.
As the whole point of the .m3u8
is to have UTF8 encoding, the BOM field seems to be redundant.
I suggest to remove adding the BOM field in Windows, and allow it to add it via the application options, independent of the operating system type (Windows or Linux).
listFix/src/main/java/listfix/io/writers/playlists/M3UWriter.java
Lines 147 to 152 in 400f973
In applying the "Fix Everything" command on a broken playlist, the error message, as depicted in the screenshot below, gets flagged.
Note: I am not sure whether I should report issues here too.
Error Screenshot:
I cannot reproduce the issue, opened a large number of playlists, and closed all of those, and then opened then again, repeated that a few times, no side-effects:
We're missing something here. It seems like this issue is due to one or other file corruption. It does work properly with a clean installation, and that *.ini file removed. What causes this issue is yet to be established. It seems to be something that was introduced in later builds.
Just now, I deleted all the json settings files, restarted the app and set it up all over again. Then reopened the 12 playlists for repair, and yet their titles were still contaminated. It looks like two superimposed titles, as I explained before.
What happened next, is surprising, I opened the Options dialogue, and tried to change the number of closest matches to find. The app immediately started to hang... I moved the indicator around... video:
Animation demonstrating the issue
Originally posted by @touwys in #51 (comment)
If a playlist is opened, and immediately closed (without changing / repairing) anything, the user is presented with a warning there are unsaved changes.
Loading .ini
files from %USERPROFILE%\.listFix()\dirLists.ini
, if it is empty:
[Media Directories]
[Media Library Directories]
[Media Library Files]
will throw an exception java.lang.IndexOutOfBoundsException: Index 3 out of bounds for length 3
at:
Which will prevent an exception while try to load new media directories:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot read the array length because "this.mediaDir" is null
at listfix.controller.GUIDriver.getMediaDirs(GUIDriver.java:149)
at listfix.view.GUIScreen._addMediaDirButtonActionPerformed(GUIScreen.java:2454)
at listfix.view.GUIScreen$16.actionPerformed(GUIScreen.java:929)
at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1972)
at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2313)
at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405)
at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:279)
at java.desktop/java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:298)
at java.desktop/java.awt.Component.processMouseEvent(Component.java:6626)
at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3389)
at java.desktop/java.awt.Component.processEvent(Component.java:6391)
at java.desktop/java.awt.Container.processEvent(Container.java:2266)
at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5001)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833)
at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4948)
at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4575)
at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4516)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2310)
at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:722)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:716)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:97)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:746)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:744)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:743)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Locale: "Select Closest Matches" > "Preview" > "Play"
While this is largely a cosmetic, not an operational issue, the partially obscured "Play" buttons displayed in the "Select Closest Matches" window, are quite bothersome. It appears to be font type and size dependent, and could certainly do with a fix once the more important issues are taken care of.
Observation: All the Play-buttons function properly, the one on the toolbar too.
Screenshot:
Whether it is by design, or not, it is possible to start and run more than one instance of listFix (2.5.1-19).
Once the selected matched track gets located by the "Open file location" action, listFix() should auto-select and highlight the matched track at its location, for easier identification. In so doing, the user does not have to page and read through reams to find it.
Playlist Directory: Action — Open a playlist:
Can the colour intensity of the progress bar for this action be intensified for increased visibility?
Reproduced this issue in versions: 2.4.0, v2.5.1
java.lang.NullPointerException: Cannot invoke "java.util.Collection.toArray()" because "c" is null
at java.util.ArrayList.addAll(ArrayList.java:670) ~[?:?]
at listfix.util.FileTypeSearch.findFiles(FileTypeSearch.java:62) ~[main/:?]
at listfix.view.GUIScreen.lambda$openTreeSelectedPlaylists$34(GUIScreen.java:1288) ~[main/:?]
at java.util.ArrayList.forEach(ArrayList.java:1511) ~[?:?]
at listfix.view.GUIScreen.openTreeSelectedPlaylists(GUIScreen.java:1285) ~[main/:?]
at listfix.view.GUIScreen._btnOpenSelectedActionPerformed(GUIScreen.java:2570) ~[main/:?]
at listfix.view.GUIScreen.lambda$initComponents$10(GUIScreen.java:905) ~[main/:?]
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1967) ~[?:?]
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2308) ~[?:?]
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405) ~[?:?]
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262) ~[?:?]
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:279) ~[?:?]
at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:298) ~[?:?]
at java.awt.Component.processMouseEvent(Component.java:6614) ~[?:?]
at javax.swing.JComponent.processMouseEvent(JComponent.java:3342) ~[?:?]
at java.awt.Component.processEvent(Component.java:6379) ~[?:?]
at java.awt.Container.processEvent(Container.java:2263) ~[?:?]
at java.awt.Component.dispatchEventImpl(Component.java:4990) ~[?:?]
at java.awt.Container.dispatchEventImpl(Container.java:2321) ~[?:?]
at java.awt.Component.dispatchEvent(Component.java:4822) ~[?:?]
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4919) ~[?:?]
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4548) ~[?:?]
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4489) ~[?:?]
at java.awt.Container.dispatchEventImpl(Container.java:2307) ~[?:?]
at java.awt.Window.dispatchEventImpl(Window.java:2769) ~[?:?]
at java.awt.Component.dispatchEvent(Component.java:4822) ~[?:?]
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:772) ~[?:?]
at java.awt.EventQueue$4.run(EventQueue.java:721) ~[?:?]
at java.awt.EventQueue$4.run(EventQueue.java:715) ~[?:?]
at java.security.AccessController.doPrivileged(AccessController.java:391) ~[?:?]
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85) ~[?:?]
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95) ~[?:?]
at java.awt.EventQueue$5.run(EventQueue.java:745) ~[?:?]
at java.awt.EventQueue$5.run(EventQueue.java:743) ~[?:?]
at java.security.AccessController.doPrivileged(AccessController.java:391) ~[?:?]
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85) ~[?:?]
at java.awt.EventQueue.dispatchEvent(EventQueue.java:742) ~[?:?]
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) ~[?:?]
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) ~[?:?]
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113) ~[?:?]
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109) ~[?:?]
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) ~[?:?]
at java.awt.EventDispatchThread.run(EventDispatchThread.java:90) ~[?:?]
The Programme Option>Playlist Directory, limits the setting to only one Playlist Directory. Instead of this existing Option serving to set this one default, extend the usefulness of the Playlist Directory by replicating some of the functionality of the Media Directories pane. In essence, this means the extended capability to open more than one playlist directory in the Playlist Directories pane, mirroring the layout and actions of the Media Directories panel, while still reserving the action to open individual playlists.
•
•
listFix() v2.6.0 | Windows 7 x64
Location: A Folder Presented in the Playlist Directory —
Steps:
Delete the folder.
Close listFix().
Restart listFix().
🗒️ The (empty) folder is still visible in the PLD. It also displays a different icon.
Assign Standardised Actions Across All Playlist Tabs via Tab Context Menu :
A. Facilitate both adjacent and non-adjacent multiple Playlist Tab selection.
B. Left-click tab enables the movement of tabs to another location on the Playlist (PL) Tab Bar.
C. Right-clicking on the selected PL-Tab invokes the PL-Tab a Context Menu, which in turn presents a set menu of extremely useful tab actions — many users are already familiar with. The following list is an example of what the menu-introduced actions can look like. It is not an exhaustive list; it merely presents an example:
On Linux Mint 20.3
Using gradle 5.6 with Java SE 13 the build was successful in 31 seconds
but encountered an exception in thread Main it has been executing for 1 hour and 22 minutes hanging on the splash screen during the initialize UI(Frozen).
Assistance would be appreciated thank you for Reviving this project.
Eliminate remain jar dependencies:
net.mariottini.swing.jar
Please, consider providing direct access to the application log-files, from the "Help" menu.
Thank you.
Retain a visible, and an operational relationship between the tab currently selected in the Playlist Editor, and the identical one presented in the Playlist Directory. Selecting one, selects the other, so they remain in unison.
When loading an M3U file, the progress bar does not work.
**Testing listFix_2.5.0**
I find myself chasing windmills. I am quite uncertain at the moment about the way certain actions, when executed, are expected to behave. The nomenclature is confusing, to say the least. Underneath, there follows is a screenshot of one such example:
For instance: Are "Exact Matches Repair" (Ctrl + M) and "Find Exact Matches" one and the same thing — only they are accessible from two different locations? As they stand now, their actions do not match.
Observation: I made two different playlists. One for a single disc taken from a multi-disc box set, and one for a number of tracks that I randomly selected. I proceeded to move the playlist out of their respective folders in order to break the list-to-track relationship, and then I copied the two test folders over to the roots of three different hard drives, plus one test-folder copied to a subdirectory on one of the drives. All the test playlists repaired properly. It should be noted that even the "Find Exact Matches" (binocular icon) action turned in fully repaired playlists. However, when the same "Find Exact Matches" (binocular icon) action is executed on previously made, older playlists, it still behaves as reported before — for all the releases tested up to now. This issue appears to be one that needs to be ironed out, and any suggestions on what the cause might be and how to proceed to nail it down, are welcome indeed.
Screenshot:
Question:
"Exact Matches Repair" (Ctrl + M): Screenshot of action-window which opens. What action is required here, which is not already available elsewhere? Does it only open a playlist to repair in some way other than what is already available?
Originally posted by @touwys in #28 (comment)
listFix() clears the Playlist Editor of all playlists upon closing — if the user chooses to discard changes made to the playlists. This action is available through the "Confirm Close" Dialogue.
A viable option here, would be to supplement the current actions in the "Confirm Close" Dialogue, with one that enables the user to preserve their current tabbed playlist layout until the app restart, when the saved playlist layout is automatically restored.
•
•
Like it says on the tin, I try to add a media directory and it does not actually add it. No error message or anything.
The only thing I can possibly consider is that the media directories I'm trying to add are not on my C;/ drive; I keep my music collection on a different hard drive. Nope it won't add C;/ drive dirs.
Specs: Windows 10 Pro 19042.1466
Introduced in #51
listFix_2.5.1-19
This could be an erroneous observation:
As soon as a playlist is deleted from the Playlist Directory, it is not simultaneously removed from the Playlist Editor. While the reverse action is not possible (for good reason), does the undeleted playlist, which stays behind in the Editor after deleting the corresponding one from the Directory, serve any further purpose?
Related to: #19
This issue is to capture regressions tests.
listFix_2.5.1-19
The "Open File Location" action is absent from the "Select Closest Matches" Dialogue box. Please add it to extend the Preview>Play review of the replacement track?
Trying to read an UTF16-BE or UTF16-LE encoded M3U file throws an Exception:
Illegal char < > at index 3: ÿþ# E X T M 3 U
java.nio.file.InvalidPathException: Illegal char < > at index 3: ÿþ# E X T M 3 U
at java.base/sun.nio.fs.WindowsPathParser.normalize(WindowsPathParser.java:182)
at java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:153)
at java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:77)
at java.base/sun.nio.fs.WindowsPath.parse(WindowsPath.java:92)
at java.base/sun.nio.fs.WindowsFileSystem.getPath(WindowsFileSystem.java:229)
at java.base/java.nio.file.Path.of(Path.java:147)
at listfix.io.playlists.PlaylistReader.processEntry(PlaylistReader.java:149)
at listfix.io.playlists.m3u.M3UReader.processEntry(M3UReader.java:221)
at listfix.io.playlists.m3u.M3UReader.readPlaylist(M3UReader.java:145)
at listfix.io.playlists.m3u.M3UReader.readPlaylist(M3UReader.java:204)
at listfix.io.playlists.m3u.M3UReaderTests.readPlaylistM3uUtf16LeWithBom(M3UReaderTests.java:60)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Currently if users experience exceptions it is looking for a needle in haystack, as exceptions are not properly logged. Secondly, many exceptions are silently caught and not captured anyway, which may result may result in other sorts of side effects as the state of the application will be unpredictable.
When a playlist, using relative path is fixed, the playlists turns back to absolute path.
This also triggers the playlist has changed, even nothing had been fixed.
Only when the playlist is saved, it normalizes the playlist back to relative path.
Playing a file may throw an error like:
VLC Is Unable to Open the MRL 'file:///C:/Users/Borewit/AppData/Local/Temp/%EF%BB%BF'.
This is caused by the fact a temporary .m3u8
file is generated, and on Windows and on Windows only the playlist is prefixed with a BOM field.
listFix/src/main/java/listfix/io/writers/playlists/M3UWriter.java
Lines 142 to 146 in 9c15414
VLC not being able to handle a BOM field is also raised against VLC: https://code.videolan.org/videolan/vlc/-/issues/21860
listFix() version 2.4.2
VLC version 3.0.18
Workaround, use foobar2000 as the default player for .m3u8
playlists.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.