├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── Tools
├── curl.exe
├── curl_64.exe
├── ffmerge.exe
├── ffmpeg.exe
├── logger.exe
├── m3u8_dl-js-master
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.md
│ ├── dist.zip
│ ├── makefile
│ ├── o
│ │ ├── play_server
│ │ │ ├── make_m3u8.coffee
│ │ │ └── play_server.coffee
│ │ └── play_static
│ │ │ └── play_m3u8.html
│ ├── package.json
│ └── src
│ │ ├── async.coffee
│ │ ├── auto_retry.coffee
│ │ ├── config.coffee
│ │ ├── decrypt.coffee
│ │ ├── dl_clip.coffee
│ │ ├── dl_speed.coffee
│ │ ├── dl_with_curl.coffee
│ │ ├── dl_with_proxy.coffee
│ │ ├── do_dl.coffee
│ │ ├── is_task_done.coffee
│ │ ├── key_host.coffee
│ │ ├── log.coffee
│ │ ├── m3u8_dl.coffee
│ │ ├── parse_m3u8.coffee
│ │ ├── show_dl_speed.coffee
│ │ ├── thread_pool.coffee
│ │ └── util.coffee
├── node.exe
└── node_64.exe
├── m3u8_dl-js_GUI.sln
├── m3u8_dl-js_GUI
├── App.config
├── Mainform.Designer.cs
├── Mainform.cs
├── Mainform.resx
├── Program.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── RealAction.cs
├── Run.cs
├── bitbug_favicon.ico
├── download_64px_1170980_easyicon.net.ico
├── m3u8_dl-js_GUI.csproj
└── m3u8_dl.cs
└── pre_processor
├── doc
└── pre_processor.md
├── pp_271-m-ts
├── pp_271_m_ts.py
└── pre_processor.meta.json
├── pp_le-decode
├── pp_le_decode.py
└── pre_processor.meta.json
├── pp_tv-line-me
├── pp_tv-line-me.py
└── pre_processor.meta.json
├── pp_wwq-any_page-url_list
├── pp_wwq_any_page.py
└── pre_processor.meta.json
└── ppp_unpacker
└── ppp_unpacker.py
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 | {one line to give the program's name and a brief idea of what it does.}
635 | Copyright (C) {year} {name of author}
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | {project} Copyright (C) {year} {fullname}
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # m3u8_dl-js_GUI
2 | A simple GUI for m3u8_dl-js...
3 |
4 | ## 公告(2018年12月25日)
5 | * 由于本人看着这上古渣代码难受,估计程序不会继续更新了。
6 |
7 | ## 如何进行批量下载
8 | #### 方法1
9 | * 将m3u8链接保存为txt文件,每行一个链接。然后将txt文件拖入程序的地址栏,点击下载。程序会自动逐个识别文件名并下载。
10 |
11 |

12 |
13 |
14 | #### 方法2(推荐)
15 | * 本地多个m3u8文件,保存在新建文件夹中,将整个文件夹拖入程序的地址栏,点击下载。程序会自动逐个识别文件名并下载。
16 |
17 |

18 |
19 |
20 | #### 方法3
21 | * 将m3u8链接下载为本地m3u8文件后再使用方法2下载。 ([辅助程序](https://github.com/nilaoda/m3u8_dl-js_GUI/releases/download/v0.4.1/m3u8.exe))
22 |
23 | ## IDM下载m3u8的几个小贴士
24 | #### 为什么点击后没反应
25 | * 将鼠标停留在按钮上数秒,按照提示操作
26 | `(若依旧无法下载,请往下看)`
27 | #### 为什么要用IDM下载
28 | * 稳定,基本不会出现花屏等现象
29 | * 强大的队列功能
30 | #### 建议使用IDM的情况
31 |
32 | `(除非你特别了解你的操作,否则我都建议先尝试IDM)`
33 | * 腾讯视频的m3u8
34 | * 爱奇艺的m3u8
35 | * 优酷4K的m3u8 `(1080P最好下载分段,m3u8存在问题 )`
36 | * 搜狐的m3u8
37 | * 芒果的m3u8
38 | #### 优酷H264的补充说明
39 | 好久没下优酷,今天(2018年10月8日)发现优酷H264的m3u8也变成真正的TS分片了,可以直接idm下载。(不是全部视频都改了,有的还是分段)
40 |
41 | 由于某些原因,优酷H264只建议下载**分段**而不是m3u8:
42 | 可以把优酷网页端appinfo接口中的各个分段url改写为一个“m3u8”然后使用下载器下载并在设置中启用**更慢的合并方式**,或者直接处理m3u8将之变成**分段**形式的“m3u8”以减少不确定性([辅助程序](https://github.com/nilaoda/m3u8_dl-js_GUI/releases/download/v0.3.0/YK-m3u82clip.exe))
43 | #### 哪些不能下载
44 | * 不能嗅探https开头的m3u8
45 | * 无法直接下载某些禁止二次请求的m3u8
46 | * 无法下载AES-128或任何形式的加密HLS流
47 | #### **奇技淫巧**
48 | 先将本地m3u8变成http形式的url,再次尝试调用IDM来下载那些本身无法被嗅探或服务器禁止二次访问的文件。
49 |
50 | 具体操作如下:
51 | * [下载HFS。](http://www.rejetto.com/hfs/?f=dl)
52 | * 下载url为m3u8文件并进行适当修改
53 | `(修改操作例如为m3u8文件手动添加Baseurl、加载外部key等. PS:事实上如果你点击过软件中的按钮,程序已经将m3u8文件下载到%temp%目录)`
54 | * 在HFS打开时,默认的IP地址即为你的本地IP地址,你可以随意指定一个端口。`(菜单-限制-防止反复连接 取消勾选)`
55 | * 此时将m3u8文件拖入左边的文件列表,然后右键刚拖入的m3u8,点击复制URL(Copy URL Address)。
56 | * 返回m3u8_dl-js_GUI,粘贴url,再次尝试IDM下载。优酷4K、搜狐的m3u8已测试通过。
57 | `(如果你有公网地址,在获取外部IP后,可以把URL分享给其他人,让他们使用下载器或IDM下载或使用移动设备直接观看。必要时在路由器端做一下端口映射哦)`
58 |
59 | 手动批量下载:
60 | * 先将url批量下载为m3u8文件 ([辅助程序](https://github.com/nilaoda/m3u8_dl-js_GUI/releases/download/v0.4.0/m3u8.exe))
61 | * 将m3u8文件放在一个文件夹中并将此文件夹拖入HFS,选择虚拟目录模式,并复制该**文件夹**URL。
62 | * 在Chrome等浏览器中打开复制好的URL,进入HFS的WEB管理界面。
63 | * **顺次**点击(下载)所列出的m3u8文件,如无意外,IDM将自动嗅探并顺序编号,此时点击悬浮框,再点击下载全部即可以队列方式将TS文件添加至IDM。
64 |
65 | 自动化:
66 | * 暂未公开的程序,敬请期待
67 | #### 下载的TS文件如何批量转换为MP4封装
68 | 使用 [MP4-Tags-Editor](https://github.com/nilaoda/MP4-Tags-Editor/releases)
69 | #### 特别提醒
70 | 在程序设置页的Headers将同样被用于请求URL,以应对某些特殊情况的IDM捕获。
71 |
72 |
73 |
74 | ## 关于“更慢的合并方式”的适用情况(#EXT-X-DISCONTINUITY引发)
75 | * 优酷的**由大分段切片的**m3u8
76 | * 搜狐、优酷、哔哩哔哩等的以**H264分段**形式组成的“m3u8”
77 |
--------------------------------------------------------------------------------
/Tools/curl.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nilaoda/m3u8_dl-js_GUI/3e94310ece920ca82735d15ac82f7001279da02d/Tools/curl.exe
--------------------------------------------------------------------------------
/Tools/curl_64.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nilaoda/m3u8_dl-js_GUI/3e94310ece920ca82735d15ac82f7001279da02d/Tools/curl_64.exe
--------------------------------------------------------------------------------
/Tools/ffmerge.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nilaoda/m3u8_dl-js_GUI/3e94310ece920ca82735d15ac82f7001279da02d/Tools/ffmerge.exe
--------------------------------------------------------------------------------
/Tools/ffmpeg.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nilaoda/m3u8_dl-js_GUI/3e94310ece920ca82735d15ac82f7001279da02d/Tools/ffmpeg.exe
--------------------------------------------------------------------------------
/Tools/logger.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nilaoda/m3u8_dl-js_GUI/3e94310ece920ca82735d15ac82f7001279da02d/Tools/logger.exe
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | /dist
3 | /tmp
4 |
5 | # node.js (npm)
6 | node_modules/
7 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/README.md:
--------------------------------------------------------------------------------
1 | # m3u8_dl-js
2 | A simple `m3u8` downloader in `node.js`.
3 |
4 |
5 | ## Build from source
6 |
7 | + **1**. Install `node.js` ()
8 |
9 | + **2**.
10 |
11 | ```
12 | $ npm install
13 | $ node ./node_modules/.bin/coffee -o dist/ src/
14 | ```
15 |
16 | **Run**
17 |
18 | ```
19 | $ node dist/m3u8_dl.js --version
20 | m3u8_dl-js version 0.2.0 test20170606 2238
21 | ```
22 |
23 |
24 | ## Usage
25 |
26 | + **m3u8_dl**
27 |
28 | ```
29 | $ node dist/m3u8_dl.js --help
30 | m3u8_dl-js [OPTIONS] M3U8
31 | Usage:
32 | -o, --output DIR Download files to this Directory
33 |
34 | -T, --thread NUM Set number of download thread (default: 1)
35 | --auto-remove Remove raw file after decrypt success
36 | --exit-on-flag Exit when FLAG file exist
37 | -H, --header NAME:VALUE Set http header (can use more than once)
38 | --proxy-http IP:PORT Set http proxy
39 | --proxy-socks5 IP:PORT Set socks5 proxy
40 | --m3u8-base-url URL Set base URL of the m3u8 file
41 |
42 | Set KEY (and IV) for AES-128 decrypt. Use HEX format, base64 format,
43 | or local binary file. Use ID to set multi-keys.
44 |
45 | --m3u8-key [ID:]HEX
46 | --m3u8-iv [ID:]HEX
47 | --m3u8-key-base64 [ID:]BASE64
48 | --m3u8-iv-base64 [ID:]BASE64
49 | --m3u8-key-file [ID::]FILE
50 | --m3u8-iv-file [ID::]FILE
51 |
52 | --version Show version of this program
53 | --help Show this help text
54 | More information online
55 | $
56 | ```
57 |
58 | + **auto_retry**
59 |
60 | ```
61 | $ node dist/auto_retry.js --help
62 | auto_retry [OPTIONS] M3U8 -- OPTIONS_FOR_M3U8_DL
63 | Usage:
64 | -o, --output DIR Download files to this Directory
65 |
66 | --retry NUM Retry times (default: 3)
67 | --use-raw-m3u8 Use `raw.m3u8` file for retry
68 | --sleep SEC Sleep seconds before next retry (default: 1)
69 |
70 | --remove-part-files Remove all `.part` files before run m3u8_dl-js
71 |
72 | --version Show version of this program
73 | --help Show this help text
74 | More information online
75 | $
76 | ```
77 |
78 | + **show_dl_speed**
79 |
80 | ```
81 | $ node dist/show_dl_speed.js --help
82 | show_dl_speed [OPTIONS] [DIR]
83 | Usage:
84 |
85 | --put-exit-flag Enable retry function (put flag file)
86 |
87 | --retry-after SEC Retry after this seconds (default: 10)
88 | --retry-hide SEC Hide retry debug info in this seconds (default: 5)
89 | --init-wait SEC Wait seconds for init_wait mode (default: 20)
90 |
91 | --version Show version of this program
92 | --help Show this help text
93 | More information online
94 | $
95 | ```
96 |
97 |
98 | ## LICENSE
99 |
100 | `GNU GPL v3+`
101 |
102 |
103 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/dist.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nilaoda/m3u8_dl-js_GUI/3e94310ece920ca82735d15ac82f7001279da02d/Tools/m3u8_dl-js-master/dist.zip
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/makefile:
--------------------------------------------------------------------------------
1 |
2 | target: build play-server
3 | .PHONY: target
4 |
5 | build:
6 | node ./node_modules/.bin/coffee -o dist/ src/
7 | .PHONY: build
8 |
9 | play-server:
10 | node ./node_modules/.bin/coffee -o dist/play_server o/play_server/
11 | .PHONY: play-server
12 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/o/play_server/make_m3u8.coffee:
--------------------------------------------------------------------------------
1 | # make_m3u8.coffee, m3u8_dl-js/o/play_server/
2 |
3 | # build a m3u8 file (text) from m3u8_dl-js 's .meta.json file
4 | make_m3u8 = (meta) ->
5 | m = meta.m3u8_info
6 |
7 | o = []
8 | # m3u8 headers
9 | o.push '#EXTM3U'
10 | o.push '#EXT-X-VERSION:3'
11 | o.push "#EXT-X-TARGETDURATION:#{m.target_duration}"
12 | # add clips
13 | for c in m.clip
14 | o.push "#EXTINF:#{c.time_s},"
15 | o.push c.name.ts
16 | # m3u8 end
17 | o.push '#EXT-X-ENDLIST'
18 |
19 | # done
20 | o.join('\n') + '\n'
21 |
22 | module.exports = make_m3u8
23 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/o/play_server/play_server.coffee:
--------------------------------------------------------------------------------
1 | # play_server.coffee, m3u8_dl-js/o/play_server/
2 | path = require 'path'
3 | http = require 'http'
4 |
5 | express = require 'express'
6 |
7 | async_ = require '../async'
8 | util = require '../util'
9 | config = require '../config'
10 |
11 | make_m3u8 = require './make_m3u8'
12 |
13 |
14 | MIME_TYPE = {
15 | m3u8: 'application/x-mpegURL'
16 | ts: 'video/MP2T'
17 | }
18 |
19 | _etc = {
20 | meta: null # loaded meta data
21 |
22 | port: 8800 # port to listen
23 | }
24 |
25 |
26 | _serve_m3u8 = (req, res) ->
27 | u = req.path
28 | if u is '/raw.m3u8'
29 | # check and load meta data
30 | if await async_.file_exist config.META_FILE
31 | text = await async_.read_file config.META_FILE
32 | _etc.meta = JSON.parse text
33 | # TODO check meta.p_version ?
34 | m3u8 = make_m3u8 _etc.meta
35 | data = Buffer.from m3u8
36 | res.status 200
37 | res.set 'Content-Type', MIME_TYPE.m3u8
38 | res.set 'Content-Length', data.length
39 | res.send data
40 | res.end()
41 | else
42 | # DEBUG
43 | console.log "DEBUG: meta file `#{config.META_FILE}` not exist !"
44 | _res_code res, 404
45 | else # `XXXX.ts` file
46 | if ! _etc.meta?
47 | _res_code res, 404
48 | else # check is one clip name
49 | for c in _etc.meta.m3u8_info.clip
50 | if u is ('/' + c.name.ts)
51 | # check file exist
52 | if await async_.file_exist c.name.ts
53 | # send this clip
54 | filepath = path.resolve c.name.ts
55 |
56 | res.sendFile filepath, {
57 | headers: {
58 | 'Content-Type': MIME_TYPE.ts
59 | }
60 | }
61 | else
62 | _res_code res, 404
63 | return
64 | _res_code res, 404
65 |
66 | _res_code = (res, code) ->
67 | text = Buffer.from "HTTP #{code} #{http.STATUS_CODES[code]}\n"
68 | res.status code
69 | res.set 'Content-Type', 'text/plain'
70 | res.set 'Content-Length', text.length
71 | res.send text
72 | res.end()
73 |
74 | _init_server = ->
75 | app = express()
76 | serve_m3u8 = express()
77 | # /m3u8_dl-js/play_m3u8/ -> _serve_m3u8
78 | serve_m3u8.all '*', (req, res) ->
79 | # check method
80 | if req.method != 'GET'
81 | _res_code res, 405
82 | return
83 | _serve_m3u8(req, res).catch( (err) ->
84 | # FIXME
85 | console.log err.stack
86 |
87 | _res_code res, 500
88 | ).then () ->
89 | # TODO
90 | app.use '/m3u8_dl-js/play_m3u8/', serve_m3u8
91 |
92 | # TODO static page ?
93 | root = express()
94 | root.all '*', (req, res) ->
95 | # check method
96 | if req.method != 'GET'
97 | _res_code res, 405
98 | return
99 | u = req.path # request url
100 | switch u
101 | #when '/'
102 | # / -> res 404
103 | # TODO for /
104 | # /m3u8 -> 302: /m3u8_dl-js/play_m3u8/raw.m3u8
105 | when '/m3u8'
106 | to = '/m3u8_dl-js/play_m3u8/raw.m3u8'
107 | res.redirect 302, to
108 | res.end()
109 | else # res 404
110 | _res_code res, 404
111 | app.use '/', root
112 | # init done
113 | app
114 |
115 | _normal = (a) ->
116 | if a.port?
117 | _etc.port = a.port
118 | # change working directory
119 | if a.root_dir?
120 | process.chdir a.root_dir
121 | # DEBUG
122 | console.log "DEBUG: working directory: #{process.cwd()}"
123 |
124 | app = _init_server()
125 | app.listen _etc.port, '127.0.0.1', () ->
126 | play_url = "http://127.0.0.1:#{_etc.port}/m3u8"
127 | if a.name?
128 | play_url += "?name=#{a.name}"
129 | # DEBUG
130 | console.log "Play at #{play_url}"
131 |
132 | _p_help = ->
133 | console.log '''
134 | play_server [OPTIONS] [DIR]
135 | Usage:
136 | --port PORT Port to listen (http server)
137 | --name NAME Add a comment for the m3u8 to play
138 |
139 | --version Show version of this program
140 | --help Show this help text
141 | More information online
142 | '''
143 |
144 | _p_arg = (args) ->
145 | rest = args
146 | _next = ->
147 | o = rest[0]
148 | rest = rest[1..]
149 | o
150 |
151 | o = {}
152 | while rest.length > 0
153 | one = _next()
154 | switch one
155 | when '--help', '--version'
156 | o.type = one
157 | when '--port'
158 | o.port = Number.parseInt _next()
159 | when '--name'
160 | o.name = _next()
161 | else # default: DIR
162 | o.root_dir = one
163 | o
164 |
165 | _start = ->
166 | a = _p_arg process.argv[2..]
167 | switch a.type
168 | when '--help'
169 | _p_help()
170 | when '--version'
171 | util.p_version()
172 | else
173 | _normal a
174 |
175 | _start()
176 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/o/play_static/play_m3u8.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nilaoda/m3u8_dl-js_GUI/3e94310ece920ca82735d15ac82f7001279da02d/Tools/m3u8_dl-js-master/o/play_static/play_m3u8.html
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "coffeescript": "^2.0.0-beta2"
4 | },
5 | "dependencies": {
6 | "express": "^4.15.3",
7 | "socks": "^1.1.10"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/async.coffee:
--------------------------------------------------------------------------------
1 | # async.coffee, m3u8_dl-js/src/
2 | # TODO use node v8.0 util.promisify ?
3 |
4 | fs = require 'fs'
5 | child_process = require 'child_process'
6 |
7 |
8 | read_file = (file_path) ->
9 | new Promise (resolve, reject) ->
10 | fs.readFile file_path, 'utf8', (err, data) ->
11 | if err
12 | reject err
13 | else
14 | resolve data
15 |
16 | read_file_byte = (filename) ->
17 | new Promise (resolve, reject) ->
18 | fs.readFile filename, (err, data) ->
19 | if err
20 | reject err
21 | else
22 | resolve data
23 |
24 | write_file = (file_path, text) ->
25 | new Promise (resolve, reject) ->
26 | fs.writeFile file_path, text, 'utf8', (err) ->
27 | if err
28 | reject err
29 | else
30 | resolve()
31 |
32 | # move file
33 | mv = (from, to) ->
34 | new Promise (resolve, reject) ->
35 | fs.rename from, to, (err) ->
36 | if err
37 | reject err
38 | else
39 | resolve()
40 |
41 | # check if file exist
42 | file_exist = (file_path) ->
43 | new Promise (resolve, reject) ->
44 | fs.access file_path, fs.constants.R_OK, (err) ->
45 | if err
46 | resolve false
47 | else
48 | resolve true
49 |
50 | # if file not exist, return null
51 | get_file_size = (file_path) ->
52 | new Promise (resolve, reject) ->
53 | fs.stat file_path, (err, stats) ->
54 | if err # never reject
55 | resolve null
56 | else
57 | resolve stats.size
58 |
59 | list_dir = (file_path) ->
60 | new Promise (resolve, reject) ->
61 | fs.readdir file_path, (err, file_list) ->
62 | if err
63 | reject err
64 | else
65 | resolve file_list
66 |
67 | mkdir = (file_path) ->
68 | new Promise (resolve, reject) ->
69 | fs.mkdir file_path, (err) ->
70 | if err
71 | reject err
72 | else
73 | resolve()
74 |
75 | # for file-lock
76 | fs_open = (file_path, flags) ->
77 | new Promise (resolve, reject) ->
78 | fs.open file_path, flags, (err, fd) ->
79 | if err
80 | reject err
81 | else
82 | resolve fd
83 |
84 | fs_close = (fd) ->
85 | new Promise (resolve, reject) ->
86 | fs.close fd, (err) ->
87 | if err
88 | reject err
89 | else
90 | resolve()
91 |
92 |
93 | # remove file
94 | rm = (file_path) ->
95 | new Promise (resolve, reject) ->
96 | fs.unlink file_path, (err) ->
97 | if err
98 | reject err
99 | else
100 | resolve()
101 |
102 | # sleep: setTimeout
103 | sleep = (time_ms) ->
104 | new Promise (resolve, reject) ->
105 | _callback = ->
106 | resolve() # never reject
107 | setTimeout _callback, time_ms
108 |
109 | # run shell command, pipe stdin -> stdin, stdout -> stdout, stderr -> stderr, return exit_code
110 | run_cmd = (args) ->
111 | new Promise (resolve, reject) ->
112 | cmd = args[0]
113 | rest = args[1..]
114 | # DEBUG
115 | console.log " run -> #{args.join(' ')}"
116 | p = child_process.spawn cmd, rest, {
117 | stdio: 'inherit'
118 | }
119 | p.on 'error', (err) ->
120 | reject err
121 | p.on 'exit', (exit_code) ->
122 | resolve exit_code
123 |
124 |
125 | module.exports = {
126 | read_file # async
127 | read_file_byte # async
128 | write_file # async
129 |
130 | mv # async
131 | rm # async
132 | file_exist # async
133 | get_file_size # async
134 | list_dir # async
135 |
136 | mkdir # async
137 | fs_open # async
138 | fs_close # async
139 |
140 | sleep # async
141 | run_cmd # async
142 | }
143 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/auto_retry.coffee:
--------------------------------------------------------------------------------
1 | # auto_retry.coffee, m3u8_dl-js/src/
2 | path = require 'path'
3 |
4 | async_ = require './async'
5 | util = require './util'
6 | log = require './log'
7 | config = require './config'
8 | dl_speed = require './dl_speed'
9 |
10 |
11 | _p_help = ->
12 | console.log '''
13 | auto_retry [OPTIONS] M3U8 -- OPTIONS_FOR_M3U8_DL
14 | Usage:
15 | -o, --output DIR Download files to this Directory
16 |
17 | --retry NUM Retry times (default: 3)
18 | --use-raw-m3u8 Use `raw.m3u8` file for retry
19 | --sleep SEC Sleep seconds before next retry (default: 1)
20 |
21 | --remove-part-files Remove all `.part` files before run m3u8_dl-js
22 |
23 | --version Show version of this program
24 | --help Show this help text
25 | More information online
26 | '''
27 |
28 | _p_arg = (args) ->
29 | rest = args
30 | _next = ->
31 | o = rest[0]
32 | rest = rest[1..]
33 | o
34 |
35 | o = {
36 | pass: [] # arguments passed to m3u8_dl
37 | }
38 | start_pass = false
39 | while rest.length > 0
40 | one = _next()
41 | if start_pass
42 | o.pass.push one
43 | continue
44 | switch one
45 | when '--help', '--version'
46 | o.type = one
47 | when '-o', '--output'
48 | config.output_dir _next()
49 |
50 | when '--retry'
51 | o.retry = Number.parseInt _next()
52 | # NaN for retry Infinity
53 | if o.retry < 1
54 | throw new Error "bad retry times #{o.retry}"
55 | when '--sleep'
56 | o.retry_sleep = JSON.parse _next()
57 | if (typeof o.retry_sleep != 'number') || Number.isNaN(o.retry_sleep) || (o.retry_sleep < 0)
58 | throw new Error "bad retry sleep #{o.retry_sleep}"
59 | when '--use-raw-m3u8'
60 | o.use_raw_m3u8 = true
61 | when '--remove-part-files'
62 | o.remove_part_files = true
63 | when '--'
64 | start_pass = true
65 |
66 | else # default: M3U8
67 | o.m3u8 = one
68 | o
69 |
70 | # for _normal tasks
71 |
72 | _etc = {
73 | pass: []
74 | retry: config.DEFAULT_RETRY_TIMES
75 | retry_sleep: config.DEFAULT_RETRY_SLEEP
76 | use_raw_m3u8: false
77 | remove_part_files: false
78 | m3u8: null
79 |
80 | retry_count: 0
81 | last_clip_done: 0
82 |
83 | all_retry_count: 0 # not reset this count
84 | }
85 |
86 | M3U8_DL_BIN = './m3u8_dl.js'
87 | _run_m3u8_dl = (args) ->
88 | # check lock file before run m3u8_dl
89 | if await async_.file_exist config.LOCK_FILE
90 | throw new Error "lock file `#{path.resolve config.LOCK_FILE}` already exist "
91 | # check remove_part_files
92 | if _etc.remove_part_files && (await async_.file_exist(config.META_FILE))
93 | log.d "AUTO_RETRY: remove all `.part` files .. . "
94 | await dl_speed.load_meta_file()
95 | clip = dl_speed.get_meta().m3u8_info.clip
96 | for c in clip
97 | if await async_.file_exist c.name.part
98 | await async_.rm c.name.part
99 |
100 | node = process.argv[0]
101 | m3u8_dl = path.join __dirname, M3U8_DL_BIN
102 |
103 | arg = [node, m3u8_dl].concat args
104 | await async_.run_cmd arg
105 |
106 | # return `true` to exit
107 | _retry_loop_one = ->
108 | # check meta file exist
109 | if ! await async_.file_exist config.META_FILE
110 | log.w "AUTO_RETRY: meta file `#{path.resolve config.META_FILE}` not exist ! "
111 | meta_exist = false
112 | task_done = false
113 | cost_retry = true
114 | else # load meta file and check task finished
115 | await dl_speed.load_meta_file()
116 | task_done = await dl_speed.update()
117 |
118 | meta_exist = true
119 | # check cost retry
120 | clip_count = dl_speed.get_clip_count()
121 | # DEBUG
122 | log.d "AUTO_RETRY: clip count: #{clip_count.done}/#{clip_count.all} (last #{_etc.last_clip_done} done)"
123 | clip_done = clip_count.done
124 | if clip_done > _etc.last_clip_done
125 | cost_retry = false
126 | else
127 | cost_retry = true
128 | _etc.last_clip_done = clip_done # update clip done
129 | # check task done
130 | if task_done
131 | log.p "AUTO_RETRY: [ OK ] task done. (retry #{_etc.all_retry_count} times)"
132 | return true
133 | # check cost retry
134 | if ! cost_retry
135 | log.d "AUTO_RETRY: reset retry count "
136 | _etc.retry_count = 0
137 | else
138 | if _etc.retry_count >= _etc.retry
139 | log.e "AUTO_RETRY: give up after retry #{_etc.retry_count} times "
140 | # TODO process.exit(1) ?
141 | return true
142 | else
143 | log.d "AUTO_RETRY: retry #{_etc.retry_count}/#{_etc.retry}"
144 | # inc retry count
145 | if cost_retry
146 | _etc.retry_count += 1
147 | _etc.all_retry_count += 1
148 | # retry sleep
149 | if _etc.retry_sleep > 0
150 | log.d "AUTO_RETRY: sleep #{_etc.retry_sleep} seconds before next retry "
151 | await async_.sleep(_etc.retry_sleep * 1e3)
152 | # retry: check use_raw_m3u8
153 | if (! meta_exist) || (! _etc.use_raw_m3u8)
154 | arg = _etc.pass.concat [_etc.m3u8]
155 | else
156 | log.d "AUTO_RETRY: enable use_raw_m3u8 "
157 |
158 | base_url = dl_speed.get_meta().m3u8_base_url
159 | if base_url?
160 | arg = ['--m3u8-base-url', base_url, config.RAW_M3U8]
161 | else
162 | arg = [config.RAW_M3U8]
163 | await _run_m3u8_dl _etc.pass.concat(arg)
164 | # not exit
165 | false
166 |
167 | _normal = (a) ->
168 | # change cwd
169 | await util.check_change_cwd true # create dir if not exist
170 | # save arguments
171 | _etc.pass = a.pass
172 | if a.retry?
173 | _etc.retry = a.retry
174 | if a.retry_sleep?
175 | _etc.retry_sleep = a.retry_sleep
176 | _etc.use_raw_m3u8 = a.use_raw_m3u8
177 | _etc.remove_part_files = a.remove_part_files
178 | _etc.m3u8 = a.m3u8
179 |
180 | # run m3u8_dl for the first time
181 | arg = _etc.pass.concat [_etc.m3u8]
182 | log.d "AUTO_RETRY: first run m3u8_dl "
183 | await _run_m3u8_dl arg
184 | # enter retry loop
185 | while true
186 | if await _retry_loop_one()
187 | break
188 |
189 | main = (argv) ->
190 | try
191 | a = _p_arg argv
192 | catch e
193 | util.p_bad_command_line()
194 | process.exit 1 # bad command line
195 | switch a.type
196 | when '--help'
197 | _p_help()
198 | when '--version'
199 | util.p_version()
200 | else
201 | await _normal a
202 | # FIX process will not exit
203 | process.exit 0
204 |
205 | _start = ->
206 | try
207 | await main(process.argv[2..])
208 | catch e
209 | # DEBUG
210 | console.log "ERROR: #{e.stack}"
211 | #throw e
212 | process.exit 1
213 | _start()
214 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/config.coffee:
--------------------------------------------------------------------------------
1 | # config.coffee, m3u8_dl-js/src/
2 |
3 | # m3u8_dl program version
4 | P_VERSION = 'm3u8_dl-js version 0.3.0-5 test20170614 2148'
5 |
6 |
7 | # local file struct
8 | LOCK_FILE = 'm3u8_dl.lock'
9 | META_FILE = 'm3u8_dl.meta.json'
10 | EXIT_FLAG_FILE = 'm3u8_dl.exit.flag' # exit download process if this file exist
11 | RAW_M3U8 = 'raw.m3u8'
12 | RAW_KEY = ['raw.', '.key'] # the key for m3u8 (support multi-keys)
13 | LIST_FILE = 'ffmpeg_merge.list'
14 | # clip file
15 | CLIP_SUFFIX_DL_PART = '.ts.part'
16 | CLIP_SUFFIX_ENCRYPTED = '.ts.encrypted'
17 | CLIP_SUFFIX_TS = '.ts'
18 | # TODO support not '.ts' in m3u8 ?
19 |
20 | # auto retry
21 | DEFAULT_RETRY_TIMES = 3
22 | DEFAULT_RETRY_SLEEP = 1
23 |
24 |
25 | _etc = {
26 | # support multi-keys
27 | m3u8_key: {}
28 | m3u8_iv: {}
29 | }
30 |
31 | # get / set proxy
32 | proxy = (p) ->
33 | if p?
34 | _etc.proxy = p
35 | _etc.proxy
36 |
37 | m3u8_base_url = (base) ->
38 | if base?
39 | _etc.m3u8_base_url = base
40 | _etc.m3u8_base_url
41 |
42 | m3u8_key = (i, k) ->
43 | if k?
44 | _etc.m3u8_key[i] = k
45 | _etc.m3u8_key[i]
46 |
47 | m3u8_iv = (i, iv) ->
48 | if iv?
49 | _etc.m3u8_iv[i] = iv
50 | _etc.m3u8_iv[i]
51 |
52 | get_all_m3u8_key = ->
53 | _etc.m3u8_key
54 |
55 | get_all_m3u8_iv = ->
56 | _etc.m3u8_iv
57 |
58 | dl_thread = (t) ->
59 | if t?
60 | _etc.dl_thread = t
61 | _etc.dl_thread
62 |
63 | # download files to (default current dir)
64 | output_dir = (d) ->
65 | if d?
66 | _etc.output_dir = d
67 | _etc.output_dir
68 |
69 | auto_remove = (a) ->
70 | if a?
71 | _etc.auto_remove = a
72 | _etc.auto_remove
73 |
74 | # TODO support set different headers for different request ?
75 | headers = (h) ->
76 | if h?
77 | _etc.headers = h
78 | _etc.headers
79 |
80 | exit_on_flag = (f) ->
81 | if f?
82 | _etc.exit_on_flag = f
83 | _etc.exit_on_flag
84 |
85 | lock_file_fd = (fd) ->
86 | if fd?
87 | _etc.lock_file_fd = fd
88 | _etc.lock_file_fd
89 |
90 | curl_bin = (bin) ->
91 | if bin?
92 | _etc.curl_bin = bin
93 | _etc.curl_bin
94 |
95 |
96 | module.exports = {
97 | P_VERSION
98 |
99 | LOCK_FILE
100 | META_FILE
101 | EXIT_FLAG_FILE
102 | RAW_M3U8
103 | RAW_KEY
104 | LIST_FILE
105 |
106 | CLIP_SUFFIX_DL_PART
107 | CLIP_SUFFIX_ENCRYPTED
108 | CLIP_SUFFIX_TS
109 |
110 | DEFAULT_RETRY_TIMES
111 | DEFAULT_RETRY_SLEEP
112 |
113 |
114 | proxy # get / set
115 | m3u8_base_url # get /set
116 | m3u8_key # get / set
117 | m3u8_iv # get / set
118 | get_all_m3u8_key
119 | get_all_m3u8_iv
120 |
121 | dl_thread # get / set
122 | output_dir # get / set
123 | auto_remove # get / set
124 | headers # get / set
125 |
126 | exit_on_flag # get / set
127 | lock_file_fd # get / set
128 |
129 | curl_bin # get / set
130 | }
131 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/decrypt.coffee:
--------------------------------------------------------------------------------
1 | # decrypt.coffee, m3u8_dl-js/src/
2 | crypto = require 'crypto'
3 |
4 | log = require './log'
5 |
6 |
7 | # decrypt m3u8 with `aes-128-cbc` (for '#EXT-X-KEY:METHOD=AES-128,')
8 | create_decrypt_stream = (key, iv) ->
9 | # if key is str, process as hex
10 | if typeof key is 'string'
11 | key = Buffer.from key, 'hex'
12 | # else: key should be a buffer
13 | if ! key instanceof Buffer
14 | throw new Error('key is not Buffer')
15 | # check key length: 128bit (8 Byte)
16 | if key.length != (128 / 8)
17 | log.w "decrypt: key length (#{key.length * 8}) is not 128bit !"
18 | # if iv in number, make it as buffer
19 | if typeof iv is 'number'
20 | i = iv
21 | # iv is 128bit, as BE
22 | iv_size = 128 / 8
23 | iv = Buffer.alloc(iv_size)
24 | iv.writeUInt32BE i, iv_size - 4
25 | # TODO more check for iv
26 |
27 | crypto.createDecipheriv 'aes-128-cbc', key, iv
28 |
29 | module.exports = {
30 | create_decrypt_stream
31 | }
32 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/dl_clip.coffee:
--------------------------------------------------------------------------------
1 | # dl_clip.coffee, m3u8_dl-js/src/
2 |
3 | fs = require 'fs'
4 |
5 | async_ = require './async'
6 | log = require './log'
7 | config = require './config'
8 | decrypt = require './decrypt'
9 | key_host = require './key_host'
10 |
11 | dl_with_proxy = require './dl_with_proxy'
12 | dl_with_curl = require './dl_with_curl'
13 |
14 |
15 | _decrypt_clip = (clip) ->
16 | # support multi-keys
17 | _do_decrypt = (c, name) ->
18 | new Promise (resolve, reject) ->
19 | r = fs.createReadStream name.encrypted
20 | w = fs.createWriteStream name.ts_tmp
21 | r.pipe(c).pipe(w)
22 | r.on 'error', (err) ->
23 | reject err
24 | c.on 'error', (err) ->
25 | reject err
26 | w.on 'error', (err) ->
27 | reject err
28 | w.on 'finish', () ->
29 | resolve()
30 | before_size = await async_.get_file_size clip.name.encrypted
31 |
32 | key_id = clip.key_id
33 | iv = config.m3u8_iv key_id
34 | if ! iv?
35 | iv = clip.media_sequence
36 | key = await key_host.get_key key_id
37 |
38 | c = decrypt.create_decrypt_stream(key, iv)
39 | await _do_decrypt c, clip.name
40 |
41 | after_size = await async_.get_file_size clip.name.ts_tmp
42 | # check decrypt success (by file_size)
43 | if ! (after_size < before_size) # after remove padding
44 | log.w "#{clip.name.ts}: decrypt MAY fail ! (before_size = #{before_size}, after_size = #{after_size}) "
45 | else # check auto remove
46 | if config.auto_remove()
47 | log.d "auto remove #{clip.name.encrypted}"
48 | await async_.rm clip.name.encrypted
49 |
50 | dl_one = (file_url, filename) ->
51 | # check use which downloader
52 | if config.curl_bin()?
53 | await dl_with_curl file_url, filename
54 | else
55 | await dl_with_proxy file_url, filename
56 |
57 | # support multi-keys
58 | dl_clip = (m3u8_info, index) ->
59 | clip = m3u8_info.clip[index]
60 | # check already exist and skip it
61 | if await async_.file_exist(clip.name.ts)
62 | log.d "dl_clip: skip exist file #{clip.name.ts}"
63 | return
64 | # load key before download clip
65 | if clip.key_id?
66 | await key_host.get_key clip.key_id
67 | # download file (support proxy)
68 | try
69 | clip_url = clip.clip_url
70 | # DEBUG
71 | log.d "dl_clip: #{clip.name.ts}: #{clip_url}"
72 | await dl_one clip_url, clip.name.part
73 | catch e
74 | log.e "dl_clip: #{clip.name.ts}: download error ! "
75 | # print stack in multi-thread mode
76 | if config.dl_thread()? && (config.dl_thread() > 1)
77 | console.log e.stack
78 | throw e
79 | # check need decrypt clip
80 | if clip.key_id?
81 | # download one file done, rename it
82 | await async_.mv clip.name.part, clip.name.encrypted
83 | await _decrypt_clip clip
84 | await async_.mv clip.name.ts_tmp, clip.name.ts
85 | else # no need to decrypt
86 | await async_.mv clip.name.part, clip.name.ts
87 | # download one clip done
88 |
89 | dl_clip.dl_one = dl_one # async
90 | module.exports = dl_clip # async
91 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/dl_speed.coffee:
--------------------------------------------------------------------------------
1 | # dl_speed.coffee, m3u8_dl-js/src/
2 |
3 | async_ = require './async'
4 | util = require './util'
5 | log = require './log'
6 | config = require './config'
7 |
8 |
9 | # global config
10 | UPDATE_TIME = 1e3 # refresh every 1s (1000 ms)
11 |
12 | _TIME_LEFT_SPEED = 5 # use 5s 's speed to calc time_left
13 |
14 |
15 | _etc = {
16 | # cache `.ts` file size
17 | ts_file_size: {}
18 |
19 | # meta file data
20 | meta: null
21 |
22 | # speed info for print
23 | speed_info: {
24 | percent: 0 # 0: 0.0 % ; 1: 100%
25 | dl_speed: 0 # unit: Byte
26 | clip_count: {
27 | done: 0
28 | doing: 0
29 | all: 0
30 | }
31 | dl_size: 0 # downloaded size (unit: Byte )
32 | all_size: null # unit: Byte ( `null` for unknow)
33 | time_left: null # unit: second ( `null` for unknow)
34 | }
35 |
36 | # history data (to make speed_info)
37 | last_dl_size: [ 0 ]
38 | }
39 |
40 |
41 | load_meta_file = ->
42 | text = await async_.read_file config.META_FILE
43 | _etc.meta = JSON.parse text
44 | # check meta.p_version
45 | if _etc.meta.p_version != config.P_VERSION
46 | log.w "META file version mismatch: #{_etc.meta.p_version}"
47 |
48 |
49 | _count_file_size = ->
50 | # get file list (current dir)
51 | l = await async_.list_dir '.'
52 | fl = {}
53 | for i in l
54 | fl[i] = true
55 | # scan clip file (in meta) and get file size
56 | m = _etc.meta.m3u8_info
57 | o = {
58 | ts: { # count of `.ts` files
59 | count: 0 # number of `.ts` files
60 | size: 0 # files size in Byte
61 | time_s: 0 # sum of clip time in second
62 | }
63 | encrypted: { # count of `.encrypted` files (not include `.ts` file)
64 | count: 0
65 | size: 0
66 | }
67 | part: { # `.part` files
68 | count: 0
69 | size: 0
70 | }
71 | time_s: 0 # count of all clips
72 | count: m.clip.length
73 | }
74 | _count_one = (clip) ->
75 | # count clip time_s
76 | clip_time_s = clip.time_s
77 | if ! (clip_time_s >= 0)
78 | clip_time_s = 0
79 | o.time_s += clip_time_s
80 | # check `.ts` file
81 | ts = clip.name.ts
82 | if fl[ts] # `.ts` file exist
83 | # cache `.ts` file size
84 | if ! _etc.ts_file_size[ts]?
85 | _etc.ts_file_size[ts] = await async_.get_file_size ts
86 | size = _etc.ts_file_size[ts]
87 |
88 | o.ts.count += 1
89 | o.ts.size += size
90 | o.ts.time_s += clip_time_s
91 | return
92 | # check `.encrypted` file
93 | en = clip.name.encrypted
94 | if fl[en]
95 | size = await async_.get_file_size en
96 |
97 | o.encrypted.count += 1
98 | o.encrypted.size += size
99 | return
100 | # check `.part` file
101 | pa = clip.name.part
102 | if fl[pa]
103 | size = await async_.get_file_size pa
104 |
105 | o.part.count += 1
106 | o.part.size += size
107 | return
108 | # count each clip
109 | for c in m.clip
110 | await _count_one c
111 | o
112 |
113 | _calc_speed_info = (fl) ->
114 | exit_flag = false
115 |
116 | si = _etc.speed_info
117 | # percent
118 | if fl.ts.count is fl.count
119 | si.percent = 1 # download done
120 | exit_flag = true
121 | else
122 | si.percent = fl.ts.count / fl.count
123 | # clip count
124 | si.clip_count.done = fl.ts.count
125 | si.clip_count.doing = fl.part.count + fl.encrypted.count
126 | si.clip_count.all = fl.count
127 | # dl_size
128 | si.dl_size = fl.ts.size + fl.encrypted.size + fl.part.size
129 | # dl_speed
130 | si.dl_speed = si.dl_size - _etc.last_dl_size[0]
131 | # process history
132 | _etc.last_dl_size = [si.dl_size].concat _etc.last_dl_size
133 | if _etc.last_dl_size.length > (_TIME_LEFT_SPEED + 1)
134 | _etc.last_dl_size.pop()
135 | # FIX last_dl_size (last 0 item) (fix calc download speed, time_left)
136 | if (_etc.last_dl_size.length > 1) && (_etc.last_dl_size[_etc.last_dl_size.length - 1] is 0)
137 | _etc.last_dl_size.pop()
138 | # all_size
139 | if fl.ts.count < 1
140 | si.all_size = null
141 | else if fl.ts.count is fl.count
142 | si.all_size = fl.ts.size
143 | else
144 | # calc average bitrate
145 | br = fl.ts.size / fl.ts.time_s
146 | si.all_size = br * fl.time_s
147 | # time_left
148 | dl_delta = si.dl_size - _etc.last_dl_size[_etc.last_dl_size.length - 1]
149 | if dl_delta < 1
150 | avg_speed = null
151 | else
152 | avg_speed = dl_delta / (_etc.last_dl_size.length - 1)
153 | if (! si.all_size?) || (! avg_speed?)
154 | si.time_left = null
155 | else
156 | rest_byte = si.all_size - si.dl_size
157 | if rest_byte <= 0
158 | si.time_left = 0
159 | else
160 | si.time_left = rest_byte / avg_speed
161 | # done
162 | exit_flag
163 |
164 | update = ->
165 | fl = await _count_file_size()
166 | _calc_speed_info fl # return exit_flag ( `true` to exit)
167 |
168 |
169 | # for print_speed
170 |
171 | # right indent
172 | _r = (o, len) ->
173 | while o.length < len
174 | o = ' ' + o
175 | o
176 |
177 | _print_percent = (percent) ->
178 | if percent is 1
179 | o = '100'
180 | else
181 | o = (percent * 1e2).toFixed 1
182 | o += ' %'
183 | # right indent self
184 | _r o, 6 # '99.9 %', '100 %'
185 |
186 | _print_dl_speed = (speed) ->
187 | U = 1024
188 | if speed <= U # < 1 KB/s
189 | o = speed + ' Byte/s'
190 | else if speed <= (U * U) # < 1 MB/s
191 | o = Math.round(speed / U) + ' KB/s'
192 | else if speed <= (U * U * U) # < 1 GB/s
193 | o = (speed / (U * U)).toFixed(1) + ' MB/s'
194 | else
195 | o = (speed / (U * U * U)).toFixed(1) + ' GB/s'
196 | # right indent self
197 | _r o, 11 # '1024 Byte/s', '1024 KB/s', '1023.9 MB/s'
198 |
199 | _print_clip_count = (c) ->
200 | o = "(#{c.done}/#{c.doing}/#{c.all})"
201 | # right indent self
202 | _r o, c.all.toString().length * 3 + ('(//)').length
203 |
204 | _print_dl_size_all = (dl_size, all_size) ->
205 | U = 1024
206 | # dl_size
207 | if dl_size < 1
208 | dl = '0'
209 | else if dl_size <= U # < 1 KB
210 | dl = dl_size + ' Byte'
211 | else if dl_size <= (U * U) # < 1 MB
212 | dl = Math.round(dl_size / U) + ' KB'
213 | else if dl_size <= (U * U * U) # < 1 GB
214 | dl = (dl_size / (U * U)).toFixed(1) + ' MB'
215 | else
216 | dl = (dl_size / (U * U * U)).toFixed(1) + ' GB'
217 | # all_size
218 | if (! all_size?) || (all_size < (U * U)) # < 1 MB
219 | all = 'unknow'
220 | else if all_size is dl_size
221 | all = dl
222 | else if all_size < (U * U * U) # < 1 GB
223 | all = Math.round(all_size / (U * U)).toFixed(1) + ' MB'
224 | else
225 | all = (all_size / (U * U * U)).toFixed(1) + ' GB'
226 | o = dl + '/ ' + all
227 | # indent self (9 + 2 + 9)
228 | _r o, 20 # '0', '1024 Byte', '1024 KB', '1023.9 MB'; 'unknow', '1023.9 MB'
229 |
230 | print_time = (time_s) ->
231 | _add_zero = (raw) ->
232 | o = raw.toString()
233 | if o.length < 2
234 | o = '0' + o
235 | o
236 |
237 | if ! time_s?
238 | o = 'UNKNOW'
239 | else
240 | if time_s != 0
241 | time_s = Number.parseInt(time_s) + 1
242 | m = Number.parseInt(time_s / 60)
243 | s = time_s - m * 60 # second
244 | h = Number.parseInt(m / 60) # hour
245 | m -= h * 60 # minute
246 |
247 | o = _add_zero(m) + ':' + _add_zero(s)
248 | if h > 0
249 | o = _add_zero(h) + ':' + o
250 | o
251 |
252 | _print_rest_time = (time_s) ->
253 | # indent self
254 | _r print_time(time_s), 8 # '00:00:00', 'UNKNOW'
255 |
256 | print_speed = ->
257 | si = _etc.speed_info
258 |
259 | prefix = '->'
260 | percent = _print_percent si.percent
261 | speed = _print_dl_speed si.dl_speed
262 | clip = _print_clip_count si.clip_count
263 | dl_all = _print_dl_size_all si.dl_size, si.all_size
264 | time = _print_rest_time si.time_left
265 |
266 | # TODO print clip_time/all_time ?
267 |
268 | # output line
269 | "#{prefix} #{percent} #{speed} #{clip} #{dl_all} #{time}"
270 |
271 | # exports function
272 |
273 | get_dl_speed = ->
274 | _etc.speed_info.dl_speed
275 |
276 | get_clip_count = ->
277 | _etc.speed_info.clip_count
278 |
279 | get_time_left = ->
280 | _etc.speed_info.time_left
281 |
282 | get_meta = ->
283 | _etc.meta
284 |
285 | module.exports = {
286 | UPDATE_TIME
287 |
288 | load_meta_file # async
289 | update # async
290 |
291 | print_speed
292 | print_time
293 |
294 | get_dl_speed
295 | get_clip_count
296 | get_time_left
297 | get_meta
298 | }
299 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/dl_with_curl.coffee:
--------------------------------------------------------------------------------
1 | # dl_with_curl.coffee, m3u8_dl-js/src/
2 |
3 | util = require './util'
4 | log = require './log'
5 | config = require './config'
6 |
7 | dl_with_curl = (file_url, filename) ->
8 | # make curl args
9 | arg = ['-Ss', '-L']
10 | # support proxy
11 | proxy = config.proxy()
12 | if proxy?
13 | switch proxy.type
14 | when 'http'
15 | arg.push '--proxy'
16 | arg.push "http://#{proxy.hostname}:#{proxy.port}"
17 | when 'socks5'
18 | arg.push '--proxy'
19 | arg.push "socks5://#{proxy.hostname}:#{proxy.port}"
20 | else
21 | throw new Error "unknow proxy.type #{proxy.type}"
22 | # add custom headers
23 | ch = config.headers()
24 | if ch?
25 | for i of ch
26 | arg.push '-H'
27 | arg.push "#{i}: #{ch[i]}"
28 | # normal options
29 | CURL_BIN = config.curl_bin()
30 | args = [CURL_BIN].concat(arg).concat ['-o', filename, file_url]
31 | # TODO remove `*.part` file before call curl ?
32 | # call curl
33 | await util.run_check args
34 |
35 | module.exports = dl_with_curl # async
36 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/dl_with_proxy.coffee:
--------------------------------------------------------------------------------
1 | # dl_with_proxy.coffee, m3u8_dl-js/src/
2 |
3 | fs = require 'fs'
4 | url = require 'url'
5 | http = require 'http'
6 | https = require 'https'
7 |
8 | socks = require 'socks'
9 |
10 | log = require './log'
11 | config = require './config'
12 |
13 |
14 | # HTTP 200 OK
15 | # HTTP 301 Moved Permanently
16 | # HTTP 302 Found
17 | # HTTP 307 Temporary Redirect
18 | # HTTP 308 Permanent Redirect
19 | _do_dl = (opt, filename, protocol) ->
20 | _req_res = (opt, protocol) ->
21 | new Promise (resolve, reject) ->
22 | _on_res = (res) ->
23 | resolve [req, res]
24 | # make http/https request
25 | switch protocol
26 | when 'http:'
27 | req = http.request opt, _on_res
28 | when 'https:'
29 | opt.rejectUnauthorized = false
30 | req = https.request opt, _on_res
31 | else
32 | reject new Error "unknow protocol `#{protocol}`"
33 | # FIXME support https-proxy ?
34 | req.on 'error', (err) ->
35 | reject err
36 | req.on 'aborted', (err) ->
37 | if err?
38 | reject err
39 | else
40 | reject new Error "aborted"
41 | # DO start request
42 | req.end()
43 | _save_file = (res, filename) ->
44 | new Promise (resolve, reject) ->
45 | # TODO process gzip compress ?
46 | # create write stream
47 | w = fs.createWriteStream filename
48 | res.pipe(w)
49 | res.on 'error', (err) ->
50 | reject err
51 | w.on 'error', (err) ->
52 | reject err
53 | w.on 'finish', () ->
54 | resolve()
55 |
56 | [req, res] = await _req_res opt, protocol
57 | # check res code
58 | code = res.statusCode
59 | switch code
60 | when 302
61 | location = res.headers['location']
62 | log.d "dl_with_proxy: #{filename}: 302 location #{location}"
63 | # download again
64 | await dl_with_proxy location, filename
65 | when 200 # http 200 OK
66 | await _save_file res, filename
67 | else # unknow code
68 | throw new Error "unknow res code `#{code}`"
69 |
70 | # do a simple http GET download a file throw the proxy config
71 | dl_with_proxy = (file_url, filename) ->
72 | info = url.parse file_url
73 | # check proxy
74 | # TODO FIXME support https with proxy ?
75 | proxy = config.proxy()
76 | if proxy?
77 | switch proxy.type
78 | when 'http'
79 | opt = {
80 | hostname: proxy.hostname
81 | port: proxy.port
82 | path: file_url
83 | headers: {
84 | 'Host': info.hostname
85 | }
86 | }
87 | when 'socks5'
88 | # TODO move socks agent creation to ./config
89 | # TODO improve: not create so much socks agent
90 | p = {
91 | ipaddress: proxy.hostname
92 | port: proxy.port
93 | type: 5 # socks5
94 | }
95 | if info.protocol is 'https:'
96 | agent = new socks.Agent {
97 | proxy: p
98 | }, true, # true for https server
99 | false
100 | else
101 | agent = new socks.Agent {
102 | proxy: p
103 | }, false, # false for http server
104 | false
105 | opt = {
106 | hostname: info.hostname
107 | port: info.port
108 | path: info.path
109 |
110 | agent: agent # use socks5 proxy
111 | }
112 | else
113 | throw new Error "unknow proxy.type #{proxy.type}"
114 | else # no proxy
115 | opt = {
116 | hostname: info.hostname
117 | port: info.port
118 | path: info.path
119 | }
120 | # add custom headers
121 | ch = config.headers()
122 | if ch?
123 | if opt.headers?
124 | Object.assign opt.headers, ch
125 | else
126 | opt.headers = ch
127 | await _do_dl opt, filename, info.protocol
128 | # FIXME close socks proxy agent when error
129 | # try close socks proxy socket
130 | if agent?
131 | agent.encryptedSocket.end()
132 |
133 |
134 | module.exports = dl_with_proxy # async
135 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/do_dl.coffee:
--------------------------------------------------------------------------------
1 | # do_dl.coffee, m3u8_dl-js/src/
2 | path = require 'path'
3 | fs = require 'fs'
4 |
5 | async_ = require './async'
6 | util = require './util'
7 | log = require './log'
8 | config = require './config'
9 | key_host = require './key_host'
10 | dl_speed = require './dl_speed'
11 |
12 | parse_m3u8 = require './parse_m3u8'
13 | dl_clip = require './dl_clip'
14 | thread_pool = require './thread_pool'
15 |
16 |
17 | _create_meta_file = (m3u8, m3u8_info) ->
18 | # support multi-keys
19 | _save_key = ->
20 | o = {
21 | m3u8_key: {}
22 | m3u8_iv: {}
23 | }
24 | keys = config.get_all_m3u8_key()
25 | ivs = config.get_all_m3u8_iv()
26 |
27 | for i of keys
28 | o.m3u8_key[i] = keys[i].toString 'base64'
29 | for i of ivs
30 | o.m3u8_iv[i] = ivs[i].toString 'base64'
31 | o
32 |
33 | o = {
34 | p_version: config.P_VERSION
35 | m3u8: m3u8
36 | cwd: process.cwd()
37 | # save base url
38 | m3u8_base_url: config.m3u8_base_url()
39 | # save KEY and IV in meta file
40 | decrypt: _save_key()
41 |
42 | m3u8_info: m3u8_info
43 | last_update: util.last_update()
44 | }
45 | text = util.print_json(o) + '\n'
46 | await util.write_file config.META_FILE, text
47 |
48 | _make_filename = (m3u8_info) ->
49 | _add_zero = (raw, len) ->
50 | while raw.length < len
51 | raw = '0' + raw
52 | raw
53 | len = m3u8_info.clip.length.toString().length
54 |
55 | _one_filename = (index) ->
56 | base = _add_zero('' + index, len)
57 | # output
58 | {
59 | part: base + config.CLIP_SUFFIX_DL_PART
60 | encrypted: base + config.CLIP_SUFFIX_ENCRYPTED
61 | ts_tmp: base + config.CLIP_SUFFIX_TS + util.WRITE_REPLACE_FILE_SUFFIX
62 | ts: base + config.CLIP_SUFFIX_TS
63 | }
64 | for i in [0... m3u8_info.clip.length]
65 | c = m3u8_info.clip[i]
66 | c.name = _one_filename i
67 | # make key filename (multi-key support)
68 | if m3u8_info.key?
69 | key = m3u8_info.key
70 | key_count = Object.keys(key).length
71 |
72 | len = key_count.toString().length
73 | N = config.RAW_KEY
74 | for i of key
75 | key[i].filename = N[0] + _add_zero(i, len) + N[1]
76 | # add key_count
77 | m3u8_info.key_count = key_count
78 | # add clip_count
79 | m3u8_info.clip_count = m3u8_info.clip.length
80 | m3u8_info
81 |
82 | # check and process clip base url
83 | _check_clip_base_url = (m3u8_info) ->
84 | # DEBUG
85 | if config.m3u8_base_url()?
86 | log.d "base URL #{config.m3u8_base_url()}"
87 | for c in m3u8_info.clip
88 | c.clip_url = util.check_merge_base_url c.url
89 | m3u8_info
90 |
91 | # create ffmpeg merge list
92 | _create_list_file = (m3u8_info) ->
93 | o = []
94 | for c in m3u8_info.clip
95 | o.push "file \'#{c.name.ts}\'"
96 | text = o.join('\n') + '\n'
97 | # write list file
98 | await util.write_file config.LIST_FILE, text
99 |
100 | _create_lock = ->
101 | # check lock file
102 | fd = await util.create_lock_file config.LOCK_FILE
103 | config.lock_file_fd fd
104 |
105 | _remove_lock = ->
106 | # close file before remove it
107 | try
108 | fs.closeSync fd
109 | catch e
110 | # ignore error
111 | try
112 | fs.unlinkSync config.LOCK_FILE
113 | catch e
114 | # ignore if file not exist
115 | # check lock file exist
116 | if fs.existsSync config.LOCK_FILE
117 | log.e "can not remove LOCK file: #{path.resolve config.LOCK_FILE}"
118 | # remove LOCK on process exit
119 | process.on 'exit', _remove_lock
120 | # FIXME other ways to exit this process
121 | process.on 'SIGINT', () ->
122 | log.d "recv SIGINT, exiting .. . "
123 | process.exit 0
124 |
125 | _check_exit_on_flag = ->
126 | _enable_exit_on_flag = ->
127 | while true
128 | # sleep 1s
129 | await async_.sleep 1e3
130 | # check flag file exist
131 | if await async_.file_exist config.EXIT_FLAG_FILE
132 | # try to remove it first
133 | await async_.rm config.EXIT_FLAG_FILE
134 | # re-check
135 | if await async_.file_exist config.EXIT_FLAG_FILE
136 | log.w "can not remove FLAG file `#{path.resolve config.EXIT_FLAG_FILE}`, not exit"
137 | else
138 | log.d "flag file `#{path.resolve config.EXIT_FLAG_FILE}` exist, exiting .. . "
139 | process.exit 1 # do exit
140 | if config.exit_on_flag()
141 | log.d "enable exit_on_flag"
142 | _enable_exit_on_flag()
143 |
144 |
145 | _count_clip_time = (m3u8_info) ->
146 | o = 0
147 | for i in m3u8_info.clip
148 | t = i.time_s
149 | if t >= 0
150 | o += t
151 | o
152 |
153 | _download_clips = (m3u8_info) ->
154 | # single-thread mode: just use loop
155 | _single_thread = ->
156 | for i in [0... m3u8_info.clip.length]
157 | await dl_clip m3u8_info, i
158 | # use thread_pool
159 | _multi_thread = (n) ->
160 | _worker = (i) ->
161 | await dl_clip m3u8_info, i
162 | # create task list
163 | t = []
164 | for i in [0... m3u8_info.clip.length]
165 | t.push i
166 | pool = thread_pool n
167 | await pool.run t, _worker
168 |
169 | video_time = dl_speed.print_time _count_clip_time(m3u8_info)
170 | log.d "download #{m3u8_info.clip.length} clips, video time #{video_time} "
171 | # check number of download thread
172 | thread_n = config.dl_thread()
173 | if (! thread_n?) || (thread_n <= 1)
174 | await _single_thread()
175 | else
176 | log.d " with #{thread_n} threads "
177 | await _multi_thread thread_n
178 |
179 |
180 | do_dl = (m3u8) ->
181 | # DEBUG
182 | if config.proxy()?
183 | log.d "use proxy #{JSON.stringify config.proxy()}"
184 | # check is remote file (http) or local file
185 | if m3u8.startsWith('http://') || m3u8.startsWith('https://')
186 | # remote file
187 | if ! config.m3u8_base_url()? # not override command line
188 | config.m3u8_base_url util.get_base_url(m3u8) # set base_url
189 | # change working directory now
190 | await util.check_change_cwd true
191 | await _create_lock()
192 | # download that m3u8 file
193 | log.d "download m3u8 file #{m3u8}"
194 | dl_tmp_file = config.RAW_M3U8 + util.WRITE_REPLACE_FILE_SUFFIX
195 | await dl_clip.dl_one m3u8, dl_tmp_file
196 | await async_.mv dl_tmp_file, config.RAW_M3U8
197 | # read that text
198 | m3u8_text = await async_.read_file config.RAW_M3U8
199 | else # local file
200 | log.d "local m3u8 file #{path.resolve m3u8}"
201 | m3u8_text = await async_.read_file m3u8
202 | # change working directory here
203 | await util.check_change_cwd true
204 | await _create_lock()
205 | # create raw m3u8 file
206 | await util.write_file config.RAW_M3U8, m3u8_text
207 | # parse m3u8 text, and create meta file
208 | m3u8_info = parse_m3u8 m3u8_text
209 | # create clip filename
210 | m3u8_info = _make_filename m3u8_info
211 | m3u8_info = _check_clip_base_url m3u8_info
212 |
213 | await _create_meta_file m3u8, m3u8_info
214 | await _create_list_file m3u8_info
215 | # DEBUG output: key_count
216 | key_count = m3u8_info.key_count
217 | if key_count? && (key_count > 0)
218 | log.d " #{key_count} keys in m3u8 file "
219 | # set key_info to key_host before start download
220 | key_host.set_key_info m3u8_info.key
221 |
222 | # support exit_on_flag
223 | _check_exit_on_flag()
224 |
225 | await _download_clips m3u8_info
226 | # not DEBUG
227 | log.p "[ OK ] all download done. "
228 |
229 |
230 | module.exports = do_dl # async
231 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/is_task_done.coffee:
--------------------------------------------------------------------------------
1 | # is_task_done.coffee, m3u8_dl-js/src/
2 | #
3 | # Usage:
4 | # is_task_done DIR
5 | #
6 | # exit 0 if task done
7 | #
8 | path = require 'path'
9 |
10 | util = require './util'
11 | log = require './log'
12 | config = require './config'
13 | dl_speed = require './dl_speed'
14 |
15 |
16 | main = (argv) ->
17 | config.output_dir argv[0]
18 | await util.check_change_cwd()
19 | log.d "load meta file #{path.resolve(config.META_FILE)}"
20 |
21 | await dl_speed.load_meta_file()
22 | exit_flag = await dl_speed.update()
23 | clip = dl_speed.get_clip_count()
24 | if exit_flag
25 | log.p "[ OK ] task done (#{clip.done}/#{clip.all})"
26 | process.exit 0 # task done
27 | else
28 | log.e "task NOT done (#{clip.done}/#{clip.all})"
29 | process.exit 1 # task NOT done
30 |
31 | _start = ->
32 | try
33 | await main(process.argv[2..])
34 | catch e
35 | # DEBUG
36 | console.log "ERROR: #{e.stack}"
37 | #throw e
38 | process.exit 1 # task NOT done
39 | _start()
40 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/key_host.coffee:
--------------------------------------------------------------------------------
1 | # key_host.coffee, m3u8_dl-js/src/
2 |
3 | async_ = require './async'
4 | util = require './util'
5 | log = require './log'
6 | config = require './config'
7 |
8 | dl_with_proxy = require './dl_with_proxy'
9 |
10 |
11 | _etc = {
12 | key_info: null
13 |
14 | # loaded KEY (value) list { key_id: key_value }
15 | key: {}
16 | }
17 |
18 |
19 | # download one key file (MUST run in single thread)
20 | _dl_one_key = (key_id) ->
21 | key = _etc.key_info[key_id]
22 | # check key method
23 | if key.method != 'AES-128'
24 | throw new Error "not support encrypt (KEY) method `#{key.method}`"
25 | # check base_url
26 | key_url = util.check_merge_base_url key.uri
27 | key_file = key.filename # local file name
28 | log.d "download key file #{key_file}: #{key_url}"
29 | await dl_with_proxy key_url, key_file
30 | # read key
31 | key_value = await async_.read_file_byte key_file
32 | # save key (cache key ?)
33 | _etc.key[key_id] = key_value
34 |
35 | _init_key_info = ->
36 | # reset load key list
37 | _etc.to_load_list = []
38 | _etc.flag_load_thread_run = false
39 |
40 | # (fake) multi-thread to single-thread (maybe use lock ?)
41 | _load_key = (key_id) ->
42 | new Promise (resolve, reject) ->
43 | _put_in_list = ->
44 | _callback = (err) ->
45 | if err
46 | reject err
47 | else
48 | resolve()
49 | one = {
50 | key_id
51 | callback: _callback
52 | }
53 | _etc.to_load_list.push one
54 |
55 | _check_load_next = ->
56 | if _etc.to_load_list.length < 1
57 | # no more keys to load
58 | _etc.flag_load_thread_run = false # reset flag
59 | return # end this (fake) thread
60 | [one, _etc.to_load_list] = [ _etc.to_load_list[0], _etc.to_load_list[1..] ]
61 | # check this key already loaded
62 | if _etc.key[one.key_id]?
63 | one.callback()
64 |
65 | # load next key
66 | setTimeout _check_load_next, 0
67 | return # not load one key twice
68 | # start load this key
69 | _dl_one_key(one.key_id).catch( (err) ->
70 | # load one key error
71 | log.e "load key #{one.key_id} failed ! "
72 | one.callback err
73 |
74 | # load next key
75 | _check_load_next()
76 | ).then () ->
77 | one.callback() # load success
78 | # load next key
79 | _check_load_next()
80 | # put task in list
81 | _put_in_list()
82 | # check load thread running
83 | if ! _etc.flag_load_thread_run
84 | _etc.flag_load_thread_run = true # set flag
85 | _check_load_next() # start load thread
86 |
87 |
88 | get_key = (key_id) ->
89 | # check config (command line) first
90 | key = config.m3u8_key key_id
91 | if key?
92 | return key
93 | # key in m3u8 file
94 | key = _etc.key[key_id]
95 | if key?
96 | return key
97 | # check key_id
98 | if ! _etc.key_info[key_id]
99 | throw new Error "no key info for key #{key_id}"
100 | # load key
101 | await _load_key key_id
102 | # NOW key should be loaded
103 | get_key key_id # call self
104 |
105 | get_iv = (key_id) ->
106 | # check config first
107 | iv = config.m3u8_iv key_id
108 | if iv?
109 | return iv
110 | # iv in m3u8 file
111 | key = _etc.key_info[key_id]
112 | if ! key?
113 | throw new Error "no key info for key #{key_id}"
114 | key.iv # may return null (if no IV)
115 |
116 | # can be use only once before start download
117 | set_key_info = (key_info) ->
118 | _etc.key_info = key_info
119 | _init_key_info()
120 |
121 | module.exports = {
122 | get_key # async
123 |
124 | get_iv
125 | set_key_info
126 | }
127 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/log.coffee:
--------------------------------------------------------------------------------
1 | # log.coffee, m3u8_dl-js/src/
2 |
3 | _PREFIX = 'm3u8_dl'
4 |
5 |
6 | # TODO print to stderr ?
7 | _p = (text) ->
8 | o = _PREFIX + text
9 | console.log o
10 |
11 |
12 | # exports
13 | d = (text) ->
14 | _p '.D: ' + text
15 | e = (text) ->
16 | _p '.ERROR: ' + text
17 | w = (text) ->
18 | _p '.WARNING: ' + text
19 | p = (text) ->
20 | _p ': ' + text
21 |
22 |
23 | module.exports = {
24 | d # DEBUG
25 | e # ERROR
26 | w # WARNING
27 |
28 | p # normal print
29 | }
30 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/m3u8_dl.coffee:
--------------------------------------------------------------------------------
1 | # m3u8_dl.coffee, m3u8_dl-js/src/
2 | path = require 'path'
3 |
4 | async_ = require './async'
5 | util = require './util'
6 | log = require './log'
7 | config = require './config'
8 |
9 | do_dl = require './do_dl'
10 |
11 |
12 | _p_help = ->
13 | console.log '''
14 | m3u8_dl-js [OPTIONS] M3U8
15 | Usage:
16 | -o, --output DIR Download files to this Directory
17 |
18 | -T, --thread NUM Set number of download thread (default: 1)
19 | --auto-remove Remove raw file after decrypt success
20 | --exit-on-flag Exit when FLAG file exist
21 | -H, --header NAME:VALUE Set http header (can use more than once)
22 | --proxy-http IP:PORT Set http proxy
23 | --proxy-socks5 IP:PORT Set socks5 proxy
24 | --dl-with-curl CURL Download clips with cURL
25 | --m3u8-base-url URL Set base URL of the m3u8 file
26 |
27 | Set KEY (and IV) for AES-128 decrypt. Use HEX format, base64 format,
28 | or local binary file. Use ID to set multi-keys.
29 |
30 | --m3u8-key [ID:]HEX
31 | --m3u8-iv [ID:]HEX
32 | --m3u8-key-base64 [ID:]BASE64
33 | --m3u8-iv-base64 [ID:]BASE64
34 | --m3u8-key-file [ID::]FILE
35 | --m3u8-iv-file [ID::]FILE
36 |
37 | --version Show version of this program
38 | --help Show this help text
39 | More information online
40 | '''
41 |
42 | _p_arg = (args) ->
43 | rest = args
44 | _next = ->
45 | o = rest[0]
46 | rest = rest[1..]
47 | o
48 |
49 | _split_ip_port = (raw) ->
50 | p = raw.split ':'
51 | o = {
52 | hostname: p[0]
53 | port: Number.parseInt p[1]
54 | }
55 |
56 | headers = {}
57 | _set_header = (raw) ->
58 | name = raw.split(':', 1)[0]
59 | value = raw[(name.length + 1) ..]
60 | headers[name] = value
61 |
62 | # support multi-keys
63 | key_file_list = []
64 | iv_file_list = []
65 | _set_key_iv = (key_iv, format, value) ->
66 | if format is 'file'
67 | key_id = 0 # default key_id
68 | i = value.indexOf '::'
69 | if i != -1
70 | key_id = Number.parseInt value[0... i]
71 | filename = value[(i + 2) ..]
72 | else
73 | filename = value
74 | one = {
75 | key_id
76 | filename
77 | }
78 | switch key_iv
79 | when 'key'
80 | key_file_list.push one
81 | when 'iv'
82 | iv_file_list.push one
83 | else
84 | key_id = 0 # default key_id
85 | i = value.indexOf ':'
86 | if i != -1
87 | key_id = Number.parseInt value[0... i]
88 | value = value[(i + 1) ..]
89 | value = Buffer.from value, format
90 | switch key_iv
91 | when 'key'
92 | config.m3u8_key value, key_id
93 | when 'iv'
94 | config.m3u8_iv value, key_id
95 |
96 | o = {}
97 | while rest.length > 0
98 | one = _next()
99 | switch one
100 | when '--help', '--version'
101 | o.type = one
102 | when '-o', '--output'
103 | config.output_dir _next()
104 |
105 | when '-T', '--thread'
106 | t = Number.parseInt _next()
107 | config.dl_thread t
108 | if t < 1
109 | throw new Error "bad thread num #{t}"
110 | when '--auto-remove'
111 | config.auto_remove true
112 | when '--exit-on-flag'
113 | config.exit_on_flag true
114 | when '-H', '--header'
115 | _set_header _next()
116 |
117 | when '--proxy-http'
118 | p = _split_ip_port _next()
119 | p.type = 'http'
120 | config.proxy p
121 | when '--proxy-socks5'
122 | p = _split_ip_port _next()
123 | p.type = 'socks5'
124 | config.proxy p
125 | when '--dl-with-curl'
126 | config.curl_bin _next()
127 |
128 | when '--m3u8-base-url'
129 | config.m3u8_base_url _next()
130 |
131 | when '--m3u8-key'
132 | _set_key_iv 'key', 'hex', _next()
133 | when '--m3u8-iv'
134 | _set_key_iv 'iv', 'hex', _next()
135 | when '--m3u8-key-base64'
136 | _set_key_iv 'key', 'base64', _next()
137 | when '--m3u8-iv-base64'
138 | _set_key_iv 'iv', 'base64', _next()
139 | when '--m3u8-key-file'
140 | _set_key_iv 'key', 'file', _next()
141 | when '--m3u8-iv-file'
142 | _set_key_iv 'iv', 'file', _next()
143 |
144 | else # default: m3u8
145 | # warning before set
146 | if o.m3u8?
147 | log.w "set M3U8 to #{one}"
148 | o.m3u8 = one
149 | if (! o.type?) && (! o.m3u8?)
150 | throw new Error "empty command line"
151 | # check set headers
152 | if Object.keys(headers).length > 0
153 | log.d "use headers #{util.print_json headers}"
154 | config.headers headers
155 | # key/iv files to load
156 | o.m3u8_key_file = key_file_list
157 | o.m3u8_iv_file = iv_file_list
158 | o
159 |
160 | _normal = (a) ->
161 | # load key/iv files (support multi-keys)
162 | key_list = a.m3u8_key_file
163 | iv_list = a.m3u8_iv_file
164 | for i in key_list
165 | log.d "load KEY (#{i.key_id}) file #{i.filename}"
166 | config.m3u8_key i.key_id, await async_.read_file_byte(i.filename)
167 | for i in iv_list
168 | log.d "load IV (#{i.key_id}) file #{i.filename}"
169 | config.m3u8_iv i.key_id, await async_.read_file_byte(i.filename)
170 |
171 | # DEBUG output: key/iv set from command line
172 | key = config.get_all_m3u8_key()
173 | iv = config.get_all_m3u8_iv()
174 | flag_debug = false
175 | o = {}
176 | if Object.keys(key).length > 0
177 | o.key = key
178 | flag_debug = true
179 | if Object.keys(iv).length > 0
180 | o.iv = iv
181 | flag_debug = true
182 | if flag_debug
183 | log.d "use KEY #{util.print_json o}"
184 |
185 | await do_dl a.m3u8
186 | # try to remove lock file
187 | fd = config.lock_file_fd()
188 | try # close file before remove it
189 | await async_.fs_close fd
190 | catch e
191 | # ignore error
192 | try
193 | await async_.rm config.LOCK_FILE
194 | catch e
195 | # ignore
196 | # check lock file exist
197 | if await async_.file_exist config.LOCK_FILE
198 | log.e "can not remove LOCK file `#{path.resolve config.LOCK_FILE}`"
199 | # FIX process will not exit
200 | process.exit 0
201 |
202 | main = (argv) ->
203 | try
204 | a = _p_arg argv
205 | catch e
206 | util.p_bad_command_line()
207 | process.exit 1 # bad command line
208 | switch a.type
209 | when '--help'
210 | _p_help()
211 | when '--version'
212 | util.p_version()
213 | else
214 | await _normal a
215 |
216 | _start = ->
217 | try
218 | await main(process.argv[2..])
219 | catch e
220 | # DEBUG
221 | console.log "ERROR: #{e.stack}"
222 | #throw e
223 | process.exit 1
224 | _start()
225 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/parse_m3u8.coffee:
--------------------------------------------------------------------------------
1 | # parse_m3u8.coffee, m3u8_dl-js/src/
2 | log = require './log'
3 |
4 |
5 | _split_lines = (raw_text) ->
6 | lines = raw_text.split '\n'
7 | o = []
8 | for l in lines
9 | l = l.trim()
10 | if l != ''
11 | o.push l
12 | o
13 |
14 | _parse_one_key = (raw_line) ->
15 | # support key line: `#EXT-X-KEY:METHOD=AES-128,URI="http://XXXX.key"`
16 |
17 | # TODO support parse IV
18 | # TODO support more format parse
19 | o = {}
20 | rest = raw_line
21 | M = 'METHOD='
22 | if rest.startsWith M
23 | rest = rest[M.length ..]
24 | i = rest.indexOf(',')
25 | if i is -1
26 | o.method = rest
27 | else
28 | o.method = rest[0...i]
29 | rest = rest[(i + 1)..]
30 |
31 | U = 'URI='
32 | if rest.startsWith U
33 | rest = rest[U.length ..]
34 | if rest.startsWith '"'
35 | o.uri = rest[1..]
36 | if o.uri.endsWith '"'
37 | o.uri = o.uri[0... o.uri.length - 1]
38 | else
39 | o.uri = rest
40 | # TODO maybe more process
41 | # else: TODO unknow format
42 | o
43 |
44 | # support multi-keys
45 | _parse_key = (key_info, raw_line) ->
46 | one = _parse_one_key raw_line
47 | # check key_id
48 | if key_info.key_id?
49 | key_info.key_id += 1
50 | else # init key_id
51 | key_info.key_id = 0
52 | key_info.key = {}
53 | # save key info
54 | key_info.key[key_info.key_id] = one
55 |
56 |
57 | parse_m3u8 = (raw_m3u8_text) ->
58 | M3U = '#EXTM3U'
59 | V = '#EXT-X-VERSION:'
60 | MS = '#EXT-X-MEDIA-SEQUENCE:'
61 | TD = '#EXT-X-TARGETDURATION:'
62 | K = '#EXT-X-KEY:'
63 | INFO = '#EXTINF:'
64 | END = '#EXT-X-ENDLIST'
65 |
66 | line = _split_lines raw_m3u8_text
67 | # check format
68 | if line[0] != M3U
69 | log.w "parse_m3u8: file format is not `#EXTM3U` "
70 |
71 | media_sequence = 0
72 | clip_s = -1 # clip time_s
73 | # support multi-keys
74 | key_info = {
75 | key_id: null
76 | key: null
77 | }
78 |
79 | o = {
80 | key: null
81 | clip: []
82 | }
83 | for l in line
84 | if l.startsWith '#'
85 | if l.startsWith INFO
86 | clip_s = JSON.parse l[INFO.length ..].split(',', 1)[0]
87 | if l.startsWith V
88 | o.version = l[V.length ..]
89 | else if l.startsWith MS
90 | media_sequence = Number.parseInt l[MS.length ..]
91 | else if l.startsWith TD
92 | o.target_duration = l[TD.length ..]
93 | else if l.startsWith K
94 | _parse_key key_info, l[K.length ..]
95 | else if l is END
96 | o.key = key_info.key # add key info
97 | return o # got file end
98 | # else: ignore this line
99 | else
100 | # is a clip file line
101 | one = {
102 | media_sequence
103 | url: l
104 | time_s: clip_s
105 | }
106 | # add key_id
107 | if key_info.key_id?
108 | one.key_id = key_info.key_id
109 | o.clip.push one
110 |
111 | clip_s = -1 # reset clip time_s
112 | media_sequence += 1
113 | # no `#EXT-X-ENDLIST`
114 | log.w "parse_m3u8: not found m3u8 end `#EXT-X-ENDLIST` "
115 | o.key = key_info.key
116 | o
117 |
118 | module.exports = parse_m3u8
119 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/show_dl_speed.coffee:
--------------------------------------------------------------------------------
1 | # show_dl_speed.coffee, m3u8_dl-js/src/
2 | path = require 'path'
3 |
4 | async_ = require './async'
5 | util = require './util'
6 | log = require './log'
7 | config = require './config'
8 | dl_speed = require './dl_speed'
9 |
10 |
11 | _p_help = ->
12 | console.log '''
13 | show_dl_speed [OPTIONS] [DIR]
14 | Usage:
15 |
16 | --put-exit-flag Enable retry function (put flag file)
17 |
18 | --retry-after SEC Retry after this seconds (default: 10)
19 | --retry-hide SEC Hide retry debug info in this seconds (default: 5)
20 | --init-wait SEC Wait seconds for init_wait mode (default: 20)
21 |
22 | --version Show version of this program
23 | --help Show this help text
24 | More information online
25 | '''
26 |
27 | _p_arg = (args) ->
28 | rest = args
29 | _next = ->
30 | o = rest[0]
31 | rest = rest[1..]
32 | o
33 |
34 | o = {}
35 | while rest.length > 0
36 | one = _next()
37 | switch one
38 | when '--help', '--version'
39 | o.type = one
40 | when '--put-exit-flag'
41 | o.put_exit_flag = true
42 |
43 | when '--retry-after'
44 | o.retry_after = Number.parseInt _next()
45 | when '--retry-hide'
46 | o.retry_hide = Number.parseInt _next()
47 | when '--init-wait'
48 | o.init_wait = Number.parseInt _next()
49 |
50 | else # default: DIR
51 | config.output_dir one
52 | o
53 |
54 | _etc = {
55 | # command line arguments
56 | put_exit_flag: false
57 | retry_after: 10
58 | retry_hide: 5
59 | init_wait: 20
60 |
61 | # show_dl_speed mode
62 | # + null Init / exit
63 | # + 'wait_meta' Wait meta file to exist
64 | # + 'wait_lock' Wait lock file to exist
65 | # + 'init_wait' Wait after first lock file exist
66 | # + 'show_speed' Normal show download speed mode
67 | # + 'speed_0' Download speed is 0
68 | mode: null
69 |
70 | # print flags
71 | not_show_speed: false
72 | # print warnings (only show once)
73 | show_lock_not_exist: false
74 |
75 | show_will_retry: false
76 |
77 | init_wait_count: 0
78 | speed_0_count: 0
79 | }
80 |
81 | # create `m3u8_dl.exit.flag` file to retry
82 | _put_flag_file = (speed_keep_s) ->
83 | log.d "now retry (speed 0 for #{speed_keep_s} s ): put exit flag file `#{path.resolve config.EXIT_FLAG_FILE}`"
84 | await util.write_file config.EXIT_FLAG_FILE, '' # create null flag file
85 |
86 |
87 | # TODO re-write show_dl_speed state process with a state machine class
88 | # some reset (mode) functions
89 |
90 | _reset_flags = ->
91 | _etc.mode = null
92 | _etc.show_lock_not_exist = false
93 |
94 | _reset_to_wait_meta = ->
95 | if _etc.mode is 'wait_meta'
96 | return # not reset if already in this mode
97 | _reset_flags()
98 | _etc.mode = 'wait_meta'
99 |
100 | _reset_to_wait_lock = ->
101 | if _etc.mode is 'wait_lock'
102 | return
103 | _reset_flags()
104 | _etc.mode = 'wait_lock'
105 | # set show_lock_not_exist
106 | _etc.show_lock_not_exist = true
107 |
108 | _reset_to_init_wait = ->
109 | if _etc.mode is 'init_wait'
110 | return
111 | _reset_flags()
112 | _etc.mode = 'init_wait'
113 | # reset init_wait_count
114 | _etc.init_wait_count = 0
115 |
116 | _reset_to_show_speed = ->
117 | if _etc.mode is 'show_speed'
118 | return
119 | _reset_flags()
120 | _etc.mode = 'show_speed'
121 |
122 | _reset_to_speed_0 = ->
123 | if _etc.mode is 'speed_0'
124 | return
125 | _reset_flags()
126 | _etc.mode = 'speed_0'
127 | # reset speed 0 count
128 | _etc.speed_0_count = 0
129 |
130 | # FIXME improve output with speed
131 | _print_enter_init_mode = ->
132 | log.d "init_wait mode for #{_etc.init_wait} s .. . "
133 |
134 | _check_mode = (lock_exist, speed) ->
135 | # reset flags
136 | _etc.show_will_retry = false
137 | # check enable put_exit_flag
138 | if ! _etc.put_exit_flag
139 | if lock_exist # just enter 'show_speed' mode
140 | _reset_to_show_speed()
141 | return
142 |
143 | # process mode: 'wait_lock' (and mode null)
144 | if (_etc.mode is 'wait_lock') || (! _etc.mode?)
145 | if ! lock_exist
146 | return # still wait
147 | else # enter 'init_wait' mode
148 | _reset_to_init_wait()
149 | # check print debug
150 | if speed is 0
151 | _print_enter_init_mode()
152 | # process mode: 'init_wait'
153 | if _etc.mode is 'init_wait'
154 | # check speed
155 | if speed is 0
156 | _etc.init_wait_count += 1
157 | if _etc.init_wait_count < _etc.init_wait
158 | return # still wait
159 | else
160 | log.d "exit init_wait mode (timeout after #{_etc.init_wait_count} s)"
161 | else
162 | log.d "exit init_wait mode (speed > 0)"
163 | # enter 'show_speed' mode
164 | _reset_to_show_speed()
165 | # NOTE there is no 'wait_exit' mode
166 |
167 | # check speed
168 | if speed is 0
169 | _reset_to_speed_0()
170 |
171 | _etc.speed_0_count += 1
172 | if _etc.speed_0_count > _etc.retry_after
173 | await _put_flag_file _etc.speed_0_count
174 | # just enter init_wait mode
175 | _reset_to_init_wait()
176 | # FIXME improve output with speed check
177 | _print_enter_init_mode()
178 | else if _etc.speed_0_count > _etc.retry_hide
179 | _etc.show_will_retry = true
180 | else
181 | _reset_to_show_speed()
182 |
183 | _show_speed = (speed) ->
184 | # check and print dl speed
185 | time_left = dl_speed.get_time_left()
186 | if (speed > 0) || time_left? # time_left is not `unknow`
187 | _etc.not_show_speed = false # reset flag
188 | else if _etc.not_show_speed
189 | return
190 | # not show 0 speed in these mode
191 | else if ['wait_meta', 'init_wait'].indexOf(_etc.mode) != -1
192 | return
193 | else
194 | _etc.not_show_speed = true
195 | console.log dl_speed.print_speed()
196 |
197 | _update = ->
198 | # check meta file exist
199 | if ! await async_.file_exist(config.META_FILE)
200 | _reset_to_wait_meta()
201 | log.d "waiting for #{path.resolve config.META_FILE}"
202 | return
203 | # check lock file exist
204 | if ! await async_.file_exist(config.LOCK_FILE)
205 | _reset_to_wait_lock()
206 | else
207 | lock_exist = true
208 | # update scan
209 | await dl_speed.load_meta_file()
210 | exit_flag = await dl_speed.update()
211 | speed = dl_speed.get_dl_speed()
212 | if (! speed?) || (speed < 1)
213 | speed = 0
214 | else # speed is not 0
215 | _etc.speed_0_count = 0 # reset speed_0_count
216 | # process mode
217 | await _check_mode lock_exist, speed
218 |
219 | _show_speed()
220 | # check and print warnings
221 | if _etc.not_show_speed # print warnings only after not show 0 speed
222 | if _etc.show_lock_not_exist
223 | _etc.show_lock_not_exist = false # only show once
224 | log.w "lock file `#{path.resolve config.LOCK_FILE}` not exist. m3u8_dl-js NOT running ?"
225 | # check print will retry
226 | if _etc.show_will_retry
227 | log.d "will retry after #{_etc.retry_after - _etc.speed_0_count} s (speed 0) .. . "
228 | # done
229 | exit_flag
230 |
231 | _normal = (a) ->
232 | # save options
233 | if a.put_exit_flag
234 | _etc.put_exit_flag = true
235 | if a.retry_after?
236 | _etc.retry_after = a.retry_after
237 | if a.retry_hide?
238 | _etc.retry_hide = a.retry_hide
239 | if a.init_wait?
240 | _etc.init_wait = a.init_wait
241 | # change cwd
242 | await util.check_change_cwd()
243 | log.d "working directory #{process.cwd()}"
244 | # init scan
245 | if await async_.file_exist config.META_FILE
246 | await dl_speed.load_meta_file()
247 | exit_flag = await dl_speed.update()
248 | if exit_flag
249 | console.log dl_speed.print_speed()
250 | return # task done, not enter main loop
251 | # main loop
252 | while true
253 | # sleep before scan again
254 | await async_.sleep dl_speed.UPDATE_TIME # sleep 1s
255 | if await _update()
256 | break # exit
257 |
258 | main = (argv) ->
259 | a = _p_arg argv
260 | # check start type
261 | switch a.type
262 | when '--help'
263 | _p_help()
264 | when '--version'
265 | util.p_version()
266 | else
267 | await _normal a
268 |
269 | _start = ->
270 | try
271 | await main(process.argv[2..])
272 | catch e
273 | # DEBUG
274 | console.log "ERROR: #{e.stack}"
275 | #throw e
276 | process.exit 1
277 | _start()
278 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/thread_pool.coffee:
--------------------------------------------------------------------------------
1 | # thread_pool.coffee, m3u8_dl-js/src/
2 |
3 | # create a new thread pool
4 | thread_pool = (pool_size) ->
5 | run = (todo_list, worker) ->
6 | o = [] # output (return) result
7 | current_run_thread_count = 0 # current running tasks count
8 | current_todo_task_id = 0 # this task is waiting todo
9 |
10 | new Promise (resolve, reject) ->
11 | # check and run a new thread
12 | check_run = ->
13 | if current_todo_task_id < todo_list.length
14 | # check start a new thread
15 | if current_run_thread_count < pool_size
16 | current_task_id = current_todo_task_id
17 | current_todo_task_id += 1
18 | current_todo = todo_list[current_task_id]
19 |
20 | # start worker
21 | worker(current_todo).catch( (err) ->
22 | # save error in result
23 | o[current_task_id] = err
24 | # end this thread
25 | current_run_thread_count -= 1
26 | # check after thread end
27 | check_run()
28 | ).then (result) ->
29 | # save result
30 | o[current_task_id] = result
31 | # end this thread
32 | current_run_thread_count -= 1
33 | check_run()
34 | # add current thread count
35 | current_run_thread_count += 1
36 | # check after start a thread
37 | check_run()
38 | # else: waiting more threads to end
39 | # else: waiting tasks to finish
40 | else if current_run_thread_count < 1
41 | resolve o # done
42 | # else: still waiting
43 | check_run()
44 | o = {
45 | run # async
46 | }
47 |
48 | module.exports = thread_pool
49 |
--------------------------------------------------------------------------------
/Tools/m3u8_dl-js-master/src/util.coffee:
--------------------------------------------------------------------------------
1 | # util.coffee, m3u8_dl-js/src/
2 | path = require 'path'
3 | url = require 'url'
4 |
5 | async_ = require './async'
6 | log = require './log'
7 | config = require './config'
8 |
9 |
10 | last_update = ->
11 | new Date().toISOString()
12 |
13 | # pretty-print JSON text
14 | print_json = (data) ->
15 | JSON.stringify data, '', ' '
16 |
17 |
18 | WRITE_REPLACE_FILE_SUFFIX = '.tmp'
19 | # atomic write-replace for a file
20 | write_file = (file_path, text) ->
21 | tmp_file = file_path + WRITE_REPLACE_FILE_SUFFIX
22 | await async_.write_file tmp_file, text
23 | await async_.mv tmp_file, file_path
24 |
25 | create_lock_file = (file_path) ->
26 | try
27 | return await async_.fs_open file_path, 'wx'
28 | catch e
29 | log.e "can not create LOCK file #{file_path} "
30 | throw e
31 |
32 | get_base_url = (full_url) ->
33 | o = url.parse full_url
34 | # clear values
35 | o.hash = null
36 | o.search = null
37 | o.query = null
38 | o.path = null
39 | o.href = null
40 |
41 | o.pathname = path.posix.dirname o.pathname
42 | url.format(o)
43 |
44 | p_bad_command_line = ->
45 | log.e 'bad command line, please try `--help` '
46 |
47 | p_version = ->
48 | # print version
49 | console.log config.P_VERSION
50 |
51 | check_change_cwd = (create_dir)->
52 | to = config.output_dir()
53 | if ! to?
54 | return
55 | to_path = path.resolve to
56 | # if output dir not exist, try to create it
57 | if (! await async_.file_exist(to)) && create_dir
58 | log.d "create dir #{to_path}"
59 | await async_.mkdir to
60 | process.chdir to
61 | cwd = process.cwd()
62 | if path.resolve(cwd) != to_path
63 | log.w "can not change current directory to `#{to_path}`, current directory is `#{cwd}`"
64 |
65 | check_merge_base_url = (raw_url) ->
66 | o = url.parse raw_url
67 | if ! o.protocol?
68 | base = config.m3u8_base_url()
69 | if ! base?
70 | log.e "no base URL for #{raw_url}"
71 | throw new Error "no base URL"
72 | # merge base url
73 | if ! base.endsWith('/')
74 | base += '/'
75 | o = new url.URL raw_url, base
76 | url.format o
77 | else
78 | raw_url
79 |
80 | # run command and check exit_code is 0 (else will throw Error)
81 | run_check = (cmd) ->
82 | exit_code = await async_.run_cmd cmd
83 | if exit_code != 0
84 | throw new Error "run command FAILED (exit_code = #{exit_code})"
85 |
86 |
87 | module.exports = {
88 | last_update
89 | print_json
90 |
91 | WRITE_REPLACE_FILE_SUFFIX
92 | write_file # async
93 |
94 | create_lock_file # async
95 | check_change_cwd # async
96 | run_check # async
97 |
98 | get_base_url
99 | p_bad_command_line
100 | p_version
101 |
102 | check_merge_base_url
103 | }
104 |
--------------------------------------------------------------------------------
/Tools/node.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nilaoda/m3u8_dl-js_GUI/3e94310ece920ca82735d15ac82f7001279da02d/Tools/node.exe
--------------------------------------------------------------------------------
/Tools/node_64.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nilaoda/m3u8_dl-js_GUI/3e94310ece920ca82735d15ac82f7001279da02d/Tools/node_64.exe
--------------------------------------------------------------------------------
/m3u8_dl-js_GUI.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26228.4
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "m3u8_dl-js_GUI", "m3u8_dl-js_GUI\m3u8_dl-js_GUI.csproj", "{FD9FCD0D-DF69-4328-9089-39A73017FE9B}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Debug|x64 = Debug|x64
12 | Debug|x86 = Debug|x86
13 | Release|Any CPU = Release|Any CPU
14 | Release|x64 = Release|x64
15 | Release|x86 = Release|x86
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {FD9FCD0D-DF69-4328-9089-39A73017FE9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {FD9FCD0D-DF69-4328-9089-39A73017FE9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {FD9FCD0D-DF69-4328-9089-39A73017FE9B}.Debug|x64.ActiveCfg = Debug|x64
21 | {FD9FCD0D-DF69-4328-9089-39A73017FE9B}.Debug|x64.Build.0 = Debug|x64
22 | {FD9FCD0D-DF69-4328-9089-39A73017FE9B}.Debug|x86.ActiveCfg = Debug|x86
23 | {FD9FCD0D-DF69-4328-9089-39A73017FE9B}.Debug|x86.Build.0 = Debug|x86
24 | {FD9FCD0D-DF69-4328-9089-39A73017FE9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {FD9FCD0D-DF69-4328-9089-39A73017FE9B}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {FD9FCD0D-DF69-4328-9089-39A73017FE9B}.Release|x64.ActiveCfg = Release|x64
27 | {FD9FCD0D-DF69-4328-9089-39A73017FE9B}.Release|x64.Build.0 = Release|x64
28 | {FD9FCD0D-DF69-4328-9089-39A73017FE9B}.Release|x86.ActiveCfg = Release|x86
29 | {FD9FCD0D-DF69-4328-9089-39A73017FE9B}.Release|x86.Build.0 = Release|x86
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | EndGlobal
35 |
--------------------------------------------------------------------------------
/m3u8_dl-js_GUI/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/m3u8_dl-js_GUI/Mainform.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 17
122 |
123 |
124 | 197, 17
125 |
126 |
127 |
128 |
129 | AAABAAEAQEAAAAEAIAAoQgAAFgAAACgAAABAAAAAgAAAAAEAIAAAAAAAAEAAABMLAAATCwAAAAAAAAAA
130 | AACbvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
131 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
132 | Mv+bvDL/nL0y/5q7Mv+LqDX/hqI2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
133 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
134 | Nv+Hozb/h6M2/4ejNv+Hozb/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
135 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
136 | Mv+bvDL/m7wy/5u8Mv+bvDL/nL0y/5q7Mv+LqTX/hqI2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
137 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
138 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
139 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
140 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/nL0y/5q7Mv+KpzX/hqE2/4ejNv+Hozb/h6M2/4ej
141 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
142 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+bvDL/m7wy/5u8
143 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
144 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/nL0y/5m6Mv+KpzX/hqI2/4ek
145 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
146 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
147 | Nv+Hozb/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
148 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/nL0y/5m6
149 | Mv+Jpjb/hqI2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
150 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
151 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
152 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
153 | Mv+bvDL/nL0y/5m5Mv+JpjX/hqI2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
154 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
155 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
156 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
157 | Mv+bvDL/m7wy/5u8Mv+bvDL/nL0y/5m5Mv+JpTX/hqI2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
158 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
159 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/m7wy/5u8
160 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
161 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/nL0y/5i4Mv+JpTX/hqI2/4ejNv+Hozb/h6M2/4ej
162 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
163 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
164 | Nv+Hozb/h6M2/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
165 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/nL4y/5e4M/+IpDb/hqI2/4ej
166 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
167 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
168 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
169 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/nL0y/5e3
170 | M/+IpDb/hqI2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
171 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
172 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/m7wy/5u8Mv+bvDL/m7wy/5u8
173 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
174 | Mv+bvDL/nL4y/5e3M/+HpDb/hqI2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
175 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
176 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/5u8
177 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
178 | Mv+bvDL/m7wy/5u8Mv+bvDL/nL4y/5a3M/+HpDb/hqI2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
179 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
180 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
181 | Nv+Hozb/h6M2/4ejNv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
182 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+auzL/nL0y/5W1M/+Hozb/hqI2/4ejNv+Hozb/h6M2/4ej
183 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
184 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
185 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
186 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5q6Mv+auzH/nr8y/5a2M/+IpDb/h6Q2/4ik
187 | Nv+IpDb/iKQ2/4ikNv+IpDb/iKQ2/4ikNv+IpDb/iKQ2/4ikNv+IpDb/iKQ2/4ikNv+IpDb/iKQ2/4ik
188 | Nv+IpDb/iKQ2/4ikNv+IpDb/iKQ2/4ikNv+IpDb/iKQ2/4aiNv+Gojb/h6M2/4ejNv+Hozb/h6M2/4ej
189 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/5u8Mv+bvDL/m7wy/5u8
190 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5q7Mv+gwjP/oMI1/5Kw
191 | NP+FoDf/haA3/4WhN/+FoDf/haE3/4WhN/+FoTf/haE3/4WhN/+FoTf/haE3/4WhN/+FoTf/haE3/4Wh
192 | N/+FoTf/haE3/4WhN/+FoTf/haE3/4WhN/+FoTf/haE3/4WhN/+FoTf/haE3/4SgNv+LqDn/i6g3/4ai
193 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
194 | Nv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5q6
195 | Mv+gwjP/gZwt/yUqE/8cHxL/HSAS/x0gEv8dIBL/HSAS/x0gEv8dIBL/HSAS/x0gEv8dIBL/HSAS/x0g
196 | Ev8dIBL/HSAS/x0gEv8dIBL/HSAS/x0gEv8dIBL/HSAS/x0gEv8dIBL/HSAS/x0gEv8dIBL/HSAS/x0h
197 | Ev8cHxL/ICUS/3GHL/+LqDf/hqI2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
198 | Nv+Hozb/h6M2/4ejNv+Hozb/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
199 | Mv+bvDL/m7wy/5u8Mv+auzH/ocM1/yYrEv89SB3/dY0y/2yCLv9uhC7/boQu/26ELv9uhC7/boQu/26E
200 | Lv9uhC7/boQu/26ELv9uhC7/boQu/26ELv9uhC7/boQu/26ELv9uhC7/boQu/26ELv9uhC7/boQu/26E
201 | Lv9uhC7/boQu/26ELv9sgi7/dY0y/z9LHf8hJhL/jKk5/4aiNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
202 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
203 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/nL4y/5i4M/8fIhP/dYwy/5KxOf+Nqjn/j606/4+t
204 | Ov+PrTr/j606/4+tOv+PrTr/j606/4+tOv+PrTr/j606/4+tOv+PrTr/j606/4+tOv+PrTr/j606/4+t
205 | Ov+PrTr/j606/4+tOv+PrTr/j606/4+tOv+PrTr/jao5/5KxOf91jDH/HB8S/4SgNv+IpDb/h6M2/4ej
206 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+bvDL/m7wy/5u8
207 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5y9Mv+ZuTP/ICQT/2yB
208 | Lv+Nqjn/ND0b/xodEP8fIxP/HiIT/x4iE/8eIhP/HiIT/x4iE/8eIhP/HiIT/x4iE/8fIxX/ICQV/yAk
209 | FP8gIxT/HyIS/x8jEv8fIxL/HyMS/x8jEv8fIxL/HyMS/x8jEv8gJBP/Gx4Q/zQ9G/+Nqjn/bIIu/x0h
210 | Ev+FoTf/iKQ2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
211 | Nv+Hozb/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
212 | Mv+cvTL/mbkz/yAjE/9sgi7/j6w6/xodEP9ZaiD/fZcr/3aOKf93jyn/d48p/3ePKf93jyn/d48p/3eP
213 | Kf94kCv/cowe/2qDHf9fdR//YXch/2l9Lv9nfCv/Z3wr/2d8LP9nfCz/Z3ws/2d8LP9meiv/bYMu/01b
214 | Iv8aHRD/jqw6/22DLv8dIBL/haE3/4ikNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
215 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
216 | Mv+bvDL/m7wy/5u8Mv+bvDL/nL0y/5i4M/8hJBP/b4Uv/5CuOv8iJhT/fZYq/6vQNv+hxDT/o8Y0/6PG
217 | NP+jxjT/o8Y0/6PGNP+lxzf/m8El/8Hadf/t9df/6/PX/7LHeP+FpSr/kK08/46sOP+Oqzj/jqw4/46s
218 | OP+OrDj/jao4/5W0O/9sgi3/IiYU/4+tOv9whi//HiET/4SgNv+IpDb/h6M2/4ejNv+Hozb/h6M2/4ej
219 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
220 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+cvTX/Gh0Q/2R4Kv+OrDr/EhMO/3qT
221 | K/+hwzP/mLgx/5q6Mv+aujL/mroy/5q6Mv+cvDX/kLQf/8LVh//5+fz/s8SG/7TFhv/6+vv/tMWH/3uZ
222 | JP+Iozn/hqE2/4ahNv+GoTb/hqE2/4WgNf+MqTj/an8t/xMVDf+Oqzr/ZXkr/xgaD/+IpDj/h6M2/4ej
223 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Gojb/m7wy/5u8
224 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+ZujH/o8Y1/1tt
225 | I/8REgz/FhgN/zQ9F/+cvTT/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+dvTb/krUg/8PXh//7+/3/m7Ja/3ua
226 | Iv97miL/m7Ja//z8/P+1xof/fJsk/4mlOv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ikN/8uNRf/FxkM/xES
227 | C/9PXyP/jqw5/4ahNf+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
228 | Nv+GoTb/jKk1/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
229 | Mv+bvDL/m7wy/5u8Mv+hwzX/fpgr/3GIJv+bvDT/nb8y/5q7Mv+bvDL/m7wy/5u8Mv+dvTb/krUg/8PX
230 | h//7+/3/mrJZ/3+dKf+KpTv/iqU7/3+dKf+asln//Pz8/7XGh/98myT/iaU6/4ejNv+Hozb/h6M2/4ei
231 | Nv+JpTb/h6M3/2J2KP9uhC7/jKk4/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
232 | Nv+Hozb/h6M2/4ejNv+FoTb/jas1/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
233 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/mrsx/6HENP+kxzX/m7wy/5q7Mv+bvDL/m7wy/5u8
234 | Mv+dvTb/krUg/8PXh//7+/3/mrJZ/3+dKf+JpTr/h6M2/4ejNv+JpTr/f50p/5qyWf/8/Pz/tcaH/3yb
235 | JP+JpTr/h6M2/4ejNv+Hozb/h6I2/4ejNv+PrTn/jao4/4aiNf+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
236 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+FoTb/jas1/5u9Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
237 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+aujL/mboy/5u8
238 | Mv+bvDL/m7wy/5u8Mv+dvTb/krUg/8PXh//7+/3/mrJZ/3+dKf+JpTr/h6M2/4ejNv+Hozb/h6M2/4ml
239 | Ov9/nSn/mrJZ//z8/P+1xof/fJsk/4mlOv+Hozb/h6M2/4ejNv+Hozb/hqE1/4aiNv+Hozb/h6M2/4ej
240 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+GoTb/jqw0/5u8Mv+bvDL/m7wy/5u8
241 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
242 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+dvTb/krUg/8PXh//7+/3/mrJZ/3+dKf+JpTr/h6M2/4ej
243 | Nv+Hozb/h6M2/4ejNv+Hozb/iaU6/3+dKf+asln//Pz8/7XGh/98myT/iaU6/4ejNv+Hozb/h6M2/4ej
244 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+FoTb/jqw0/5y9
245 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
246 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+dvTb/krUg/8PXh//7+/3/mrJZ/3+d
247 | Kf+JpTr/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+JpTr/f50p/5qyWf/8/Pz/tcaH/3yb
248 | JP+JpTr/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
249 | Nv+GoTb/jqw0/5y9Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
250 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+dvTb/krYg/8PX
251 | h//7+/3/mrJZ/3+dKf+JpTr/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ml
252 | Ov9/nSn/mrJZ//z8/P+1xof/fJsk/4mlOv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
253 | Nv+Hozb/h6M2/4ejNv+GoTb/j600/5y9Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
254 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
255 | Mv+dvTX/krYg/8PXh//7/P3/mrJZ/3+dKf+JpTr/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
256 | Nv+Hozb/h6M2/4ejNv+Hozb/iaU6/3+dKf+asln//Pz8/7XGh/98myT/iaU5/4ejNv+Hozb/h6M2/4ej
257 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+FoTb/j600/5y9Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
258 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
259 | Mv+bvDL/m7wy/5u8Mv+cvTT/k7cj/8PXh//7/P3/mrJZ/3+dKf+JpTr/h6M2/4ejNv+Hozb/h6M2/4ej
260 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+JpTr/f50p/5qyWf/8/Pz/tseH/36c
261 | J/+JpDj/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+GoTb/kK80/5y9Mv+bvDL/m7wy/5u8
262 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
263 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+cvTT/lbgm/7rRdf/7/P3/m7Ja/3+dKf+JpTr/h6M2/4ej
264 | Nv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ml
265 | Ov9/nSn/m7Ja//v8/P+rv3X/gJ4r/4ikOP+Hozb/h6M2/4ejNv+Hozb/h6M2/4ejNv+GoTb/ka80/5y+
266 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
267 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/nL0z/5W4Kf/r8tn/scOA/32b
268 | JP+Mpz7/iaQ5/4mkOf+JpTn/iKQ3/4ejNv+GoTn/h6M3/4ejNv+Hozb/h6M2/4ejNv+Hozb/h6M2/4ej
269 | Nv+IpDf/iaU5/4mkOf+JpDn/jKc+/32bJP+yxIH/5uzY/4CeLP+IpDf/h6M2/4ejNv+Hozb/h6M2/4ej
270 | Nv+GoTb/ka80/5y+Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
271 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5y9
272 | M/+WuCn/7PPb/6/Bff92lhj/f50n/32cJf99nCX/fpwm/4ShMf+Iozf/jasq/4mlM/+Hozf/h6M2/4ej
273 | Nv+Hozb/h6M2/4ejNv+Hozf/hKEx/36cJv99myX/fZsl/36cJ/91lRj/sMJ9/+ft2v+Bni3/iKQ3/4ej
274 | Nv+Hozb/h6M2/4ejNv+GoTb/kbA0/5y+Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
275 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
276 | Mv+bvDL/m7wy/5u8Mv+cvTX/lbgm/73TfP/4+fr/xdKj/8DOmv/Bz5z/wc+c/8DOmf+asVb/g6Ez/15w
277 | h/99mEr/iaYy/4ejN/+Hozb/h6M2/4ejNv+IpDf/hKEw/5mxV//Az5n/wtGc/8PRnP/B0Jr/x9Sj//n6
278 | +P+vwnz/gJ4q/4ikOf+Hozb/h6M2/4ejNv+Gojb/krA0/5y+Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
279 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
280 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5y8M/+WuSr/ts5s/9vouP/a57f/2+e4/9bk
281 | r//9//v/ucmM/3ydLP8vNeT/cotg/4yoLf+Gojj/h6M2/4ejNv+Hozb/iaQ5/36cJ/+5yY7/+fr5/8zY
282 | rv/S3Lf/0dy2/9Ldt/+mu23/gp8t/4ikN/+Hozb/h6M2/4ejNv+Gojb/k7Iz/52+Mv+bvDL/m7wy/5u8
283 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
284 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/nL00/5a4
285 | J/+StiD/krYi/5S3JP+KsBL/2ue5/7rJjv98nSr/Nz7V/3ONXf+LqC7/hqI4/4ejNv+Hozb/h6M2/4mk
286 | Of9+nCb/usqP/9LcuP90lBb/f50o/32bJv98myT/gZ4r/4ikOP+Hozb/h6M2/4ejNv+Gojb/k7Iz/5y+
287 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
288 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
289 | Mv+bvDL/m7wy/5u8Mv+cvTT/nb01/529Nf+evjf/lbgn/9/qw/+6yY7/fJ0q/zY92P9zjV3/i6gt/4ai
290 | OP+Hozb/h6M2/4ejNv+JpDn/fpwm/7rKkP/Y4cH/gJ4q/4qmPP+JpDn/iaU5/4ikOP+Hozb/h6M2/4ej
291 | Nv+Gojb/k7Mz/5y+Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
292 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
293 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/nL01/5O3I//f6sH/usmO/3yd
294 | Kv82Pdj/c41d/4uoLf+Gojj/h6M2/4ejNv+Hozb/iaQ5/36cJv+7ypD/1+DA/36cJ/+JpDn/h6M2/4ej
295 | Nv+Hozb/h6M2/4ejNv+Gojb/k7Mz/52+Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
296 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
297 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5y9
298 | Nf+TtyP/3+rB/7rJjv98nSr/Nj3Y/3ONXf+LqC3/hqI4/4ejNv+Hozb/h6M2/4mkOf9+nCb/u8qQ/9fg
299 | wP9+nCf/iaQ5/4ejNv+Hozb/h6M2/4ejNv+Hozb/lLQz/5y+Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
300 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
301 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
302 | Mv+bvDL/m7wy/5u8Mv+cvTX/k7cj/9/qwf+6yY7/fJ0q/zY92P9zjV3/i6gt/4aiOP+Hozb/h6M2/4ej
303 | Nv+JpDn/fpwm/7vKkP/X4MD/fpwn/4mkOf+Hozb/h6M2/4ejNv+Hojb/lbQz/52+Mv+bvDL/m7wy/5u8
304 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
305 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
306 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/nL01/5O3I//f6sH/usmO/3ydKv82Pdj/c41d/4uo
307 | Lf+Gojj/h6M2/4ejNv+Hozb/iaQ5/36cJv+7ypD/1+DA/36cJ/+JpDn/h6M2/4ejNv+Hozb/lbUz/5y+
308 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
309 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
310 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5y9Nf+TtyP/3+rB/7rJ
311 | jv98nSr/Nj3Y/3ONXf+LqC3/hqI4/4ejNv+Hozb/h6M2/4mkOf9+nCb/u8qQ/9fgwP9+nCf/iaU5/4ej
312 | Nv+Hozb/lrUz/5y+Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
313 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
314 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
315 | Mv+cvTX/k7cj/9/qwf+6yY7/fJ0q/zc+1f9zjV3/i6gt/4aiOP+Hozb/h6M2/4ejNv+JpDn/fpwm/7vK
316 | kP/X4MD/fpwn/4ikOf+Hozb/lrUz/5y+Mv+buzL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
317 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
318 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
319 | Mv+bvDL/m7wy/5u8Mv+bvDL/nL01/5O3I//f6sH/usmO/3ydK/8xNuH/cotg/4yoLf+Gojj/h6M2/4ej
320 | Nv+Hozb/iaQ5/36cJv+7ypD/1+DA/36bJ/+JpTj/l7cz/5y+Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
321 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
322 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
323 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5y9Nf+TtyT/3urA/7rJjv9+nSv/UGOp/3qW
324 | U/+LpzL/h6M5/4ikOP+IpDj/iKQ4/4qlOv9/nSj/u8uP/9bfv/9/nSf/mLg1/5y+Mv+buzL/m7wy/5u8
325 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
326 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
327 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+cvTX/k7Yi/+Hs
328 | xf+5yI7/d5ca/4mnKf+DoSv/gp8u/4KfLf+Cny3/gp8t/4KfLf+EoTH/d5ca/7rKjv/a48T/jbAj/56/
329 | Nf+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
330 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
331 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
332 | Mv+bvDL/nL01/5S3JP/D14f/8vTw/6G3Zv+bsV3/nbNf/52zXv+ds17/nbNe/52zXv+ds17/nLJc/6G3
333 | Zf/y9PD/w9eG/5W4JP+cvTX/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
334 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
335 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
336 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/mLot/8zdmv/w9eb/6/He/+zy3//s8t//7PLf/+zy
337 | 3//s8t//7PLf/+vx3v/w9eb/zN2a/5i6Lf+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
338 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
339 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
340 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+UtyX/mrsy/529
341 | N/+cvTb/nL02/5y9Nv+cvTb/nL02/5y9Nv+dvTf/mrsy/5S3Jf+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
342 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
343 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
344 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
345 | Mv+bvDL/nL01/5u8Mf+avDD/mrww/5q8MP+avDD/mrww/5q8MP+avDD/mrww/5u8Mf+cvTX/m7wy/5u8
346 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
347 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
348 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
349 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wz/5u8M/+bvDP/m7wz/5u8M/+bvDP/m7wz/5u8
350 | M/+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
351 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
352 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
353 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
354 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
355 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
356 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
357 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
358 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
359 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
360 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
361 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
362 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
363 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
364 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
365 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
366 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
367 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
368 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
369 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
370 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
371 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
372 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
373 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
374 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
375 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
376 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
377 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
378 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
379 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
380 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
381 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
382 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
383 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
384 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
385 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
386 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
387 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
388 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
389 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
390 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
391 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
392 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
393 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
394 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
395 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
396 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
397 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
398 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
399 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
400 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
401 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
402 | Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8Mv+bvDL/m7wy/5u8
403 | Mv+bvDL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
404 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
405 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
406 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
407 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
408 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
409 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
410 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
411 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
412 |
413 |
414 |
--------------------------------------------------------------------------------
/m3u8_dl-js_GUI/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.Windows.Forms;
6 |
7 | namespace M3U8_DL_GUI
8 | {
9 | static class Program
10 | {
11 | ///
12 | /// 应用程序的主入口点。
13 | ///
14 | [STAThread]
15 | static void Main(string[] args)
16 | {
17 | Application.EnableVisualStyles();
18 | Application.SetCompatibleTextRenderingDefault(false);
19 | if (args.Length == 0)
20 | Application.Run(new Form1());
21 | else
22 | Application.Run(new Form1(args));
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/m3u8_dl-js_GUI/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // 有关程序集的一般信息由以下
6 | // 控制。更改这些特性值可修改
7 | // 与程序集关联的信息。
8 | [assembly: AssemblyTitle("m3u8_dl_GUI")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("nilaoda")]
12 | [assembly: AssemblyProduct("m3u8_dl_GUI")]
13 | [assembly: AssemblyCopyright("Copyright © 2018")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | //将 ComVisible 设置为 false 将使此程序集中的类型
18 | //对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型,
19 | //请将此类型的 ComVisible 特性设置为 true。
20 | [assembly: ComVisible(false)]
21 |
22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
23 | [assembly: Guid("fd9fcd0d-df69-4328-9089-39a73017fe9b")]
24 |
25 | // 程序集的版本信息由下列四个值组成:
26 | //
27 | // 主版本
28 | // 次版本
29 | // 生成号
30 | // 修订号
31 | //
32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
33 | // 方法是按如下所示使用“*”: :
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/m3u8_dl-js_GUI/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // 此代码由工具生成。
4 | // 运行时版本:4.0.30319.42000
5 | //
6 | // 对此文件的更改可能会导致不正确的行为,并且如果
7 | // 重新生成代码,这些更改将会丢失。
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace m3u8_dl_GUI.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// 一个强类型的资源类,用于查找本地化的字符串等。
17 | ///
18 | // 此类是由 StronglyTypedResourceBuilder
19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
21 | // (以 /str 作为命令选项),或重新生成 VS 项目。
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// 返回此类使用的缓存的 ResourceManager 实例。
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("m3u8_dl_GUI.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// 使用此强类型资源类,为所有资源查找
51 | /// 重写当前线程的 CurrentUICulture 属性。
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/m3u8_dl-js_GUI/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/m3u8_dl-js_GUI/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // 此代码由工具生成。
4 | // 运行时版本:4.0.30319.42000
5 | //
6 | // 对此文件的更改可能会导致不正确的行为,并且如果
7 | // 重新生成代码,这些更改将会丢失。
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace m3u8_dl_GUI.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.0.1.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/m3u8_dl-js_GUI/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/m3u8_dl-js_GUI/RealAction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Windows.Forms;
9 |
10 | namespace M3U8_DL_GUI
11 | {
12 | // 1.定义委托
13 | public delegate void DelReadStdOutput(string result);
14 | public delegate void DelReadErrOutput(string result);
15 |
16 | public partial class Form1 : Form
17 | {
18 | // 2.定义委托事件
19 | public event DelReadStdOutput ReadStdOutput;
20 | public event DelReadErrOutput ReadErrOutput;
21 |
22 | private void Init()
23 | {
24 | //3.将相应函数注册到委托事件中
25 | ReadStdOutput += new DelReadStdOutput(ReadStdOutputAction);
26 | ReadErrOutput += new DelReadErrOutput(ReadErrOutputAction);
27 | }
28 |
29 | private void RealAction(string StartFileName)
30 | {
31 | Process CmdProcess = new Process();
32 | CmdProcess.StartInfo.FileName = StartFileName; // 命令
33 | //CmdProcess.StartInfo.Arguments = StartFileArg; // 参数
34 |
35 | CmdProcess.StartInfo.CreateNoWindow = true; // 不创建新窗口
36 | CmdProcess.StartInfo.UseShellExecute = false;
37 | CmdProcess.StartInfo.RedirectStandardInput = true; // 重定向输入
38 | CmdProcess.StartInfo.RedirectStandardOutput = true; // 重定向标准输出
39 | CmdProcess.StartInfo.RedirectStandardError = true; // 重定向错误输出
40 | //CmdProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
41 |
42 | CmdProcess.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
43 | CmdProcess.ErrorDataReceived += new DataReceivedEventHandler(p_ErrorDataReceived);
44 |
45 | CmdProcess.EnableRaisingEvents = true; // 启用Exited事件
46 | CmdProcess.Exited += new EventHandler(CmdProcess_Exited); // 注册进程结束事件
47 |
48 | CmdProcess.Start();
49 | PID_1 = CmdProcess.Id; //PID
50 | CmdProcess.BeginOutputReadLine();
51 | CmdProcess.BeginErrorReadLine();
52 |
53 | // 如果打开注释,则以同步方式执行命令,此例子中用Exited事件异步执行。
54 | //CmdProcess.WaitForExit();
55 | }
56 |
57 | private void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
58 | {
59 | if (e.Data != null)
60 | {
61 | // 4. 异步调用,需要invoke
62 | this.Invoke(ReadStdOutput, new object[] { e.Data });
63 | }
64 | }
65 |
66 | private void p_ErrorDataReceived(object sender, DataReceivedEventArgs e)
67 | {
68 | if (e.Data != null)
69 | {
70 | this.Invoke(ReadErrOutput, new object[] { e.Data });
71 | }
72 | }
73 |
74 | private void ReadStdOutputAction(string result)
75 | {
76 | textBox_Info.AppendText(result + "\r\n");
77 | if (result.Contains("m3u8_dl.D: download") && result.Contains("clips, video time"))
78 | {
79 | label_dur_clip_out.Text = "m3u8信息:" + result
80 | .Replace("m3u8_dl.D: download", "")
81 | .Replace("clips, video time", "个分片; 预计时长:");
82 | }
83 | }
84 |
85 | private void ReadErrOutputAction(string result)
86 | {
87 | textBox_Info.AppendText(result + "\r\n");
88 | }
89 |
90 | private void CmdProcess_Exited(object sender, EventArgs e)
91 | {
92 | button_Download.Enabled = true; //启用下载按钮
93 |
94 | //杀死读取下载速度的进程
95 |
96 | EndProcessTree(PID_2);
97 | PID_1 = 0;
98 | PID_2 = 0;
99 |
100 | bool isNormalExited = File.Exists(downpath + "\\isNormalExited"); //是否是正常停止进程
101 |
102 | if (isNormalExited && checkBox_auto_merge.Checked == true && checkBox_del_afterMerge.Checked == true)
103 | {
104 | try
105 | {
106 | DirectoryInfo directoryInfo = new DirectoryInfo(downpath);
107 | directoryInfo.Delete(true);
108 | }
109 | catch (Exception) { }
110 | }
111 |
112 | MessageBox.Show("命令执行结束!", "m3u8_dl_GUI", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly); // 执行结束后触发
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/m3u8_dl-js_GUI/Run.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Windows.Forms;
8 |
9 | namespace M3U8_DL_GUI
10 | {
11 | public partial class Form1 : Form
12 | {
13 | private void RealAction2(string StartFileName)
14 | {
15 | Process p = new Process();
16 | p.StartInfo.FileName = StartFileName;
17 | p.StartInfo.UseShellExecute = false; //必须为false才能重定向输出
18 | p.StartInfo.RedirectStandardInput = true;
19 | p.StartInfo.RedirectStandardOutput = true;
20 | p.StartInfo.RedirectStandardError = true;
21 | p.StartInfo.CreateNoWindow = true;
22 | p.OutputDataReceived += new DataReceivedEventHandler(P_OutputDataReceived);
23 | p.Start();
24 | PID_2 = p.Id; //PID
25 | p.BeginOutputReadLine();
26 | }
27 |
28 | private delegate void AddMessageHandler(string msg);
29 |
30 | private void P_OutputDataReceived(object sender, DataReceivedEventArgs e)
31 | {
32 | AddMessageHandler handler = delegate (string msg)
33 | {
34 | this.label_speed_out.Text = msg;
35 | };
36 | if (this.label_speed_out.InvokeRequired)
37 | this.label_speed_out.Invoke(handler, e.Data);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/m3u8_dl-js_GUI/bitbug_favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nilaoda/m3u8_dl-js_GUI/3e94310ece920ca82735d15ac82f7001279da02d/m3u8_dl-js_GUI/bitbug_favicon.ico
--------------------------------------------------------------------------------
/m3u8_dl-js_GUI/download_64px_1170980_easyicon.net.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nilaoda/m3u8_dl-js_GUI/3e94310ece920ca82735d15ac82f7001279da02d/m3u8_dl-js_GUI/download_64px_1170980_easyicon.net.ico
--------------------------------------------------------------------------------
/m3u8_dl-js_GUI/m3u8_dl-js_GUI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {FD9FCD0D-DF69-4328-9089-39A73017FE9B}
8 | WinExe
9 | Properties
10 | m3u8_dl_GUI
11 | m3u8_dl_GUI
12 | v4.6
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 | download_64px_1170980_easyicon.net.ico
37 |
38 |
39 | true
40 | bin\x64\Debug\
41 | DEBUG;TRACE
42 | full
43 | x64
44 | prompt
45 | MinimumRecommendedRules.ruleset
46 | true
47 |
48 |
49 | bin\x64\Release\
50 | TRACE
51 | true
52 | pdbonly
53 | x64
54 | prompt
55 | MinimumRecommendedRules.ruleset
56 | true
57 |
58 |
59 | true
60 | bin\x86\Debug\
61 | DEBUG;TRACE
62 | full
63 | x86
64 | prompt
65 | MinimumRecommendedRules.ruleset
66 | true
67 |
68 |
69 | bin\x86\Release\
70 | TRACE
71 | true
72 | pdbonly
73 | x86
74 | prompt
75 | MinimumRecommendedRules.ruleset
76 | true
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | Form
95 |
96 |
97 | Form
98 |
99 |
100 | Mainform.cs
101 |
102 |
103 |
104 |
105 | Form
106 |
107 |
108 | Form
109 |
110 |
111 | Mainform.cs
112 |
113 |
114 | ResXFileCodeGenerator
115 | Resources.Designer.cs
116 | Designer
117 |
118 |
119 | True
120 | Resources.resx
121 | True
122 |
123 |
124 | SettingsSingleFileGenerator
125 | Settings.Designer.cs
126 |
127 |
128 | True
129 | Settings.settings
130 | True
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
148 |
--------------------------------------------------------------------------------
/m3u8_dl-js_GUI/m3u8_dl.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Text.RegularExpressions;
8 | using System.Threading.Tasks;
9 | using System.Windows.Forms;
10 |
11 | namespace M3U8_DL_GUI
12 | {
13 | public partial class Form1 : Form
14 | {
15 | //建立动态数组存放文件名数据
16 | ArrayList fileName = new ArrayList();
17 |
18 | //x64
19 | string curl_path = System.Windows.Forms.Application.StartupPath + @"\Tools\curl_64.exe";
20 | string node_path = System.Windows.Forms.Application.StartupPath + @"\Tools\node_64.exe";
21 |
22 | //x86
23 | //string curl_path = System.Windows.Forms.Application.StartupPath + @"\Tools\curl.exe";
24 | //string node_path = System.Windows.Forms.Application.StartupPath + @"\Tools\node.exe";
25 |
26 | public string GetCommand()
27 | {
28 | string ffmerge_path = System.Windows.Forms.Application.StartupPath + @"\Tools\ffmerge.exe"; //ffmerge路径
29 | string ffmpeg_path = System.Windows.Forms.Application.StartupPath + @"\Tools\ffmpeg.exe"; //ffmpeg路径
30 | //string logger_path = System.Windows.Forms.Application.StartupPath + "\\Tools\\logger.exe"; //logger路径
31 | string m3u8_dl_path = System.Windows.Forms.Application.StartupPath + @"\Tools\m3u8_dl-js-master\dist\m3u8_dl.js"; //主程序路径
32 | string is_task_done_path = System.Windows.Forms.Application.StartupPath + @"\Tools\m3u8_dl-js-master\dist\is_task_done.js";
33 | string auto_retry_path = System.Windows.Forms.Application.StartupPath + @"\Tools\m3u8_dl-js-master\dist\auto_retry.js";
34 | string m3u8_path = textBox_Adress.Text.Trim().Replace("%","%%");
35 | //string log_name = System.Windows.Forms.Application.StartupPath + "\\Logs\\Log-" + System.DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + ".txt"; //日志文件位置
36 | string command = "";
37 | string file_path = label_outPutPath.Text + "\\" + textBox_filename.Text;
38 | //string command = "\"" + logger_path + "\" -format raw -log \"" + log_name + "\" : "; //最终命令
39 | command += "(echo =======================-m3u8_dl-js 版本信息-=======================)";
40 | command += " && (\"" + node_path + "\"" + " \"" + m3u8_dl_path + "\" --version)";
41 | command += " && (echo =======================-m3u8_dl-js 命令开始-=======================)";
42 | command += " && (" + "\"" + node_path + "\"" + " \"" + auto_retry_path + "\" -o \"" + label_outPutPath.Text + "\\" + textBox_filename.Text.Replace("%", "%%") + "\""
43 | + " --retry " + numericUpDown_retry.Text + " --sleep " + numericUpDown_sleep.Text + " --remove-part-files --use-raw-m3u8 \"" + m3u8_path + "\""
44 | + " -- " //下面是传递给m3u8_dl的参数
45 | + "--thread " + numericUpDown_threads.Text + " ";
46 | if (checkBox_auto_remove.Checked == true) { command += "--auto-remove "; }
47 | if (checkBox_add_base_url.Checked == true && textBox_Base_url.Text != "" && !textBox_Base_url.Text.Contains(" "))
48 | {
49 | command += "--m3u8-base-url " + textBox_Base_url.Text.Trim().Replace("%", "%%") + " ";
50 | }
51 | if (checkBox_proxy.Checked == true && textBox_proxy.Text != "")
52 | {
53 | if (radioButton_http.Checked == true) { command += "--proxy-http \"" + textBox_proxy.Text.Trim() + "\" "; }
54 | if (radioButton_socks5.Checked == true) { command += "--proxy-socks5 \"" + textBox_proxy.Text.Trim() + "\" "; }
55 | }
56 | if (checkBox_Headers.Checked == true && textBox_Headers.Text != "")
57 | {
58 | string[] headers = GetHeader(textBox_Headers.Text.Replace("%", "%%"));
59 | for(int i = 0; i < headers.Length; i++)
60 | {
61 | command += "--header \"" + headers[i] + "\" ";
62 | }
63 | }
64 | if (checkBox_down_with_curl.Checked == true)
65 | {
66 | command += "--dl-with-curl \"" + curl_path + "\" ";
67 | }
68 |
69 | command += "--exit-on-flag)";
70 | command += " && (" + "echo =======================-检测是否完成下载-=======================)";
71 | command += " && (" + "\"" + node_path + "\"" + " \"" + is_task_done_path + "\" \"" + label_outPutPath.Text + "\\" + textBox_filename.Text + "\")"; //检测是否完成下载
72 |
73 | //合并选项
74 | if (checkBox_auto_merge.Checked == true)
75 | {
76 | command += " && (cd /d \"" + label_outPutPath.Text + "\")";
77 | command += " && (" + "echo =======================-合并命令开始-=======================)";
78 |
79 | /*
80 | if (radioButton_mergeBinary.Checked == true)
81 | {
82 | command += " && " //+ "\"" + logger_path + "\" -format raw -append -log \"" + log_name + "\" : "
83 | + "copy /b \"" + label_outPutPath.Text + "\\" + textBox_filename.Text + "\\*.ts\" \""
84 | + label_outPutPath.Text + "\\" + textBox_filename.Text + ".ts\"";
85 | }
86 | if (radioButton_mergeFFmpeg.Checked == true)
87 | {
88 | string file_path = label_outPutPath.Text + "\\" + textBox_filename.Text;
89 | command += " && cd /d \"" + file_path + "\" "
90 | + " && \"" + ffmpeg_path + "\" -f concat -safe 0 -i \""
91 | + label_outPutPath.Text + "\\" + textBox_filename.Text
92 | + "\\ffmpeg_merge.list\" -threads 0 -c copy -f mpegts \""
93 | + label_outPutPath.Text + "\\" + textBox_filename.Text + ".ts\"";
94 | command += " && cd /d .."; //调回上级目录
95 | }
96 | */
97 |
98 | //通过 ffmpeg_merge.list 读取全部文件,写入数组,生成转换文件
99 | //修改了 do_dl.js 的 114 行 o.push(`${c.name.ts}`);
100 |
101 | if (checkBox_IsNewMerge.Checked == true)
102 | {
103 | command += " && (echo [转换分片中...])";
104 | command += " && (for /f \"usebackq tokens=*\" %%i in (\"" + file_path + "\\ffmpeg_merge.list\") do ("; //usebackq参数,防止引号中的内容被当成字符串
105 | command += " \"" + ffmpeg_path + "\" -y -i \"" + file_path + "\\%%i\" -loglevel quiet -map 0 -c copy -f mpegts -bsf:v h264_mp4toannexb \"" + file_path + "\\[TS]%%i\"";
106 | command += " && del \"" + file_path + "\\%%i\"";
107 | command += " && move \"" + file_path + "\\[TS]%%i\" \"" + file_path + "\\%%i\" >nul))";
108 | }
109 |
110 | /*
111 | command += " && (echo [合并分片中...])"
112 | + " && (copy /b \"" + file_path + "\\*.ts\" \""
113 | + file_path + ".ts\")";
114 | */
115 |
116 | command += " && pushd \"" + file_path + "\""; //进入此目录,为合并做准备
117 |
118 | //转换格式部分
119 | if (comboBox_convertFormat.SelectedIndex != 0)
120 | {
121 | if (comboBox_convertFormat.SelectedIndex == 1) //MP4
122 | {
123 | /*
124 | command += " && (" + "\"" + ffmpeg_path + "\"" + " -threads 0 -i \"" + file_path + ".ts\""
125 | + " -c copy -y -bsf:a aac_adtstoasc \"" + file_path + ".mp4\")";
126 | */
127 | command += " && (" + "\"" + ffmerge_path + "\" \""
128 | + ffmpeg_path + "\" \"" + file_path
129 | + "\\ffmpeg_merge.list\" \"" + file_path + "\" MP4)";
130 | }
131 | if (comboBox_convertFormat.SelectedIndex == 2) //MKV
132 | {
133 | command += " && (" + "\"" + ffmerge_path + "\" \""
134 | + ffmpeg_path + "\" \"" + file_path
135 | + "\\ffmpeg_merge.list\" \"" + file_path + "\" MKV)";
136 | }
137 | if (comboBox_convertFormat.SelectedIndex == 3) //FLV
138 | {
139 | command += " && (" + "\"" + ffmerge_path + "\" \""
140 | + ffmpeg_path + "\" \"" + file_path
141 | + "\\ffmpeg_merge.list\" \"" + file_path + "\" FLV)";
142 | }
143 | if (comboBox_convertFormat.SelectedIndex == 4) //TS
144 | {
145 | command += " && (" + "\"" + ffmerge_path + "\" \""
146 | + ffmpeg_path + "\" \"" + file_path
147 | + "\\ffmpeg_merge.list\" \"" + file_path + "\" TS)";
148 | }
149 | }
150 | else //不转换格式,采用旧版二进制合并方案
151 | {
152 | command += " && (" + "echo =======================-合并命令开始-=======================)";
153 | command += " && (copy /b \"" + file_path + "\\*.ts\" \""
154 | + file_path + ".ts\")";
155 | }
156 |
157 | /*
158 | if (checkBox_del_afterMerge.Checked == true)
159 | {
160 | command += " && (" + "echo =======================-删除命令开始-=======================)";
161 | command += " && (ping 127.0.0.1 -n 3 >nul)"; //延迟3秒执行
162 | command += " && (" //+ "\"" + logger_path + "\" -format raw -append -log \"" + log_name + "\" : "
163 | + "rd /s /Q \"" + file_path + "\")";
164 | }
165 | */
166 | }
167 |
168 | command += " && (type nul >\"" + file_path + "\\isNormalExited\")"; //所有命令执行完毕后生成空白文件用于判断是否为正常退出进程
169 |
170 | return command.Replace(":\\\\", ":\\"); //简单粗暴解决根目录问题
171 | }
172 |
173 | public string GetSpeedCommand(string batPath)
174 | {
175 | string show_dl_speed_path = System.Windows.Forms.Application.StartupPath + "\\Tools\\m3u8_dl-js-master\\dist\\show_dl_speed.js";
176 | string command = "";
177 | command = "\"" + node_path + "\" \"" + show_dl_speed_path + "\" --retry-after 30 --retry-hide 5 --put-exit-flag \""
178 | + label_outPutPath.Text + "\\" + textBox_filename.Text + "\"";
179 | string file_path = batPath.Replace("m3u8_dl_GUI_Download", "m3u8_dl_GUI_Show_Speed");
180 | StreamWriter writer = new StreamWriter(file_path, false, Encoding.Default); //false代表替换而不是追加
181 | writer.WriteLine("@echo off");
182 | writer.Write(command);
183 | writer.Close();
184 |
185 | return file_path;
186 | }
187 |
188 | private static string[] GetHeader(string ArrayStr)
189 | {
190 | string StrJson = ArrayStr;
191 | return StrJson.Split('|');
192 | }
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/pre_processor/doc/pre_processor.md:
--------------------------------------------------------------------------------
1 |
4 | (`zh_CN`)
5 |
6 | # `m3u8_dl-js_GUI` 预处理程序 接口规范
7 | `version 0.1.0`
8 |
9 |
10 | ## 1. 概述
11 |
12 | `m3u8_dl-js_GUI` (以下 简称 `GUI`) 使用 **预处理程序** (`pre_processor`) 来
13 | 增强 其功能.
14 |
15 | 预处理程序 可以使用 **任意** 编程语言 实现, 目前 至少支持:
16 |
17 | + `python 3` (python 3.6 运行环境)
18 | + `nodejs` (node v8.0 运行环境)
19 |
20 |
21 | ## 2. 命令行参数 (格式)
22 |
23 | `GUI` 使用 如下格式 调用 预处理程序:
24 |
25 | ```
26 | PRE_PROCESSOR OUT_FILE RAW_FILE [URL] [-- OPTIONS]
27 | ```
28 |
29 | + **`PRE_PROCESSOR`**
30 | 预处理程序 (可执行程序 或者 主入口文件)
31 |
32 | + **`OUT_FILE`**
33 | 输出文件 (文件名 / 绝对路径)
34 |
35 | `GUI` 期望 预处理程序 生成的文件.
36 |
37 | + **`RAW_FILE`**
38 | 原始文件 (文件名 / 绝对路径)
39 |
40 | 预处理程序 的 输入文件.
41 |
42 | + **`URL`** (可选)
43 | 原始文件 对应 的 原始 URL
44 |
45 | **注**: 如果 没有 原始 URL, 那么 没有 此项 参数.
46 |
47 | + **`--`** (可选)
48 | 自定义参数 的 分隔符
49 |
50 | + **`OPTIONS`** (可选)
51 | 传递给 预处理程序 的 自定义参数 (可能 有 多个)
52 |
53 | 例如, `GUI` 使用 如下方式 (命令行 参数) 调用一个 预处理程序:
54 |
55 | ```
56 | python pp1.py out1 raw.m3u8 "http://test.org/1.m3u8" -- XXX YYY
57 | ```
58 |
59 |
60 | ## 3. 调用 预处理程序
61 |
62 | `GUI` 使用如下 步骤 调用 预处理程序:
63 |
64 | + (1) `GUI` 下载 原始 URL 对应的 文件, 作为 原始文件 (`RAW_FILE`)
65 |
66 | + (2) `GUI` 使用 原始文件 路径, 原始 URL 等 作为 参数, 调用 预处理程序
67 |
68 | + (3) `GUI` 分析/处理 预处理程序 生成的 输出文件 (`OUT_FILE`)
69 |
70 |
71 | ## 4. 输出文件
72 |
73 | 预处理程序 可以根据 `GUI` 指定的 路径 (`OUT_FILE`), 输出 以下类型的 文件:
74 |
75 | + (1) **不输出** 文件
76 | 预处理程序 没有 生成 输出文件.
77 |
78 | 当 预处理程序 发生错误 时, 可以 不生成 输出文件.
79 | 此时 `GUI` 将从 预处理程序 (进程) 的
80 | 标准输出 (`stdout`) / 标准错误输出 (`stderr`) 获取 错误信息.
81 |
82 | + (2) 输出 **`OUT_FILE.error`** 文件
83 |
84 | 发生错误 时, 预处理程序 也可以生成一个 文本文件, 里面含有 错误信息.
85 |
86 | + (3) 输出 **`OUT_FILE.m3u8`** 文件
87 | 生成 标准格式 的 m3u8 文件.
88 |
89 | + (4) 输出 **`OUT_FILE.list`** 文件
90 | 生成 列表 文件.
91 |
92 | 此 文本文件 格式 如下:
93 |
94 | ```
95 | # 说明 (可选)
96 | URL
97 | ```
98 |
99 | 每连续的 2 行 为 一组, 表示 列表中的 一项.
100 | 以 `#` 字符 起始的 说明 行, 可以 省略.
101 |
102 | **注**: 空白行 会被 忽略.
103 |
104 | + (5) 输出 **`OUT_FILE.format`** 文件
105 | 生成 多格式选择 文件.
106 |
107 | 此 文本文件 格式 如下:
108 |
109 | ```
110 | # 格式说明
111 | OPTIONS
112 | ```
113 |
114 | 类似 *列表* 文件, 每连续 2 行 为 一组.
115 |
116 | `OPTIONS` 为 需要 传递给 预处理程序 的 自定义参数.
117 |
118 |
119 | ## 5. 预处理程序 打包格式
120 |
121 | 为了方便 预处理程序 的 分发 以及 用户 使用, 可以 对 预处理程序 打包.
122 |
123 | 打包格式 为 标准的 **`zip` 压缩文件**, 后缀为 **`.zip.ppp`** .
124 | (其中 `ppp` 表示 `Pre Processor Package` )
125 |
126 | 压缩包 根目录 下 有一个 **`pre_processor.meta.json`** 文件, 描述 预处理程序 有关信息.
127 | 此文件 格式如下:
128 |
129 | ```
130 | // 下面 使用 javascript 语法描述 文件格式, 实际使用时 请使用 JSON 语法
131 | {
132 | pp_version: '0.1.0', // 预处理程序 接口规范 版本号 (当前 是 '0.1.0')
133 |
134 | id: '', // 预处理程序 标识
135 | name: '', // 此 预处理程序 用户友好 的 描述性 名称
136 | version: '', // 预处理程序 版本号
137 |
138 | type: '', // 预处理程序 类型, 可用 值 有: 'python3', 'nodejs', 'exe'
139 | main: '', // 主文件 (可执行程序 或者 入口文件) 路径
140 | // (相对于此 `pre_processor.meta.json` 文件)
141 |
142 | // 以上 项目 是 **必须** 的, 以下 项目 是 *可选* 的
143 | license: '', // LICENSE
144 | author: '', // 作者
145 | copyright: '',
146 | home: '', // 项目 (此 预处理程序) 主页 URL
147 |
148 | // 此外, 还允许使用 自定义项目, 自定义项目 请以 `_` (下划线) 起始
149 | }
150 | ```
151 |
152 |
153 |
154 |
155 |
156 |
--------------------------------------------------------------------------------
/pre_processor/pp_271-m-ts/pp_271_m_ts.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3.6
2 | # pp_271_m_ts.py, m3u8_dl-js_GUI/pre_processor/pp_271-m-ts/
3 | #
4 | # upstream:
5 | # LICENSE: GNU GPL v3+
6 | #
7 | import sys
8 |
9 | import re
10 | import time, hashlib
11 | import urllib.request
12 | import json
13 |
14 |
15 | def r1(r, text):
16 | m = re.search(r, text)
17 | if m:
18 | return m.group(1)
19 |
20 | def dl_json(url):
21 | print('D: GET ' + url)
22 | r = urllib.request.urlopen(url)
23 | return json.load(r)
24 |
25 |
26 | _BID_TO_FORMAT = {
27 | 19 : {
28 | 'index': 7,
29 | 'name': '4K (h265)',
30 | },
31 | 18 : {
32 | 'index': 6,
33 | 'name': '1080p (h265)',
34 | },
35 | 10 : {
36 | 'index': 5,
37 | 'name': '4K', # h264
38 | },
39 | 5 : {
40 | 'index': 4,
41 | 'name': '1080p', # h264
42 | },
43 |
44 | 17 : {
45 | 'index': 3,
46 | 'name': '720p (h265)',
47 | },
48 | 4 : {
49 | 'index': 2,
50 | 'name': '720p', # h264
51 | },
52 |
53 | 21 : {
54 | 'index': 1,
55 | 'name': '540p (h265)',
56 | },
57 | 2 : {
58 | 'index': 0,
59 | 'name': '540p',
60 | },
61 | 1 : {
62 | 'index': -1,
63 | 'name': '360p',
64 | },
65 | 96 : {
66 | 'index': -2,
67 | 'name': '210p',
68 | },
69 | }
70 |
71 |
72 | class Mts(object):
73 |
74 | @staticmethod
75 | def vms_url(tvid, vid):
76 | src = '76f90cbd92f94a2e925d83e8ccd22cb7'
77 | key = 'd5fb4bd9d50c4be6948c97edd7254b0e'
78 |
79 | t = int(time.time() * 1e3)
80 | sc = hashlib.new('md5', bytes(str(t) + key + vid, 'utf-8')).hexdigest()
81 | return 'http://cache.m.iqiyi.com/tmts/{0}/{1}/?t={2}&sc={3}&src={4}'.format(tvid, vid, t, sc, src)
82 |
83 | @staticmethod
84 | def get_info(html, url):
85 | o = {}
86 | o['tvid'] = \
87 | r1(r'#curid=(.+)_', url) or \
88 | r1(r'tvid=([^&]+)', url) or \
89 | r1(r'data-player-tvid="([^"]+)"', html)
90 | o['vid'] = \
91 | r1(r'#curid=.+_(.*)$', url) or \
92 | r1(r'vid=([^&]+)', url) or \
93 | r1(r'data-player-videoid="([^"]+)"', html)
94 | o['title'] = r1('([^<]+)', html).split('-')[0]
95 | return o
96 |
97 | @staticmethod
98 | def parse_vms(vms, tvid):
99 | OK_CODE = 'A00000'
100 |
101 | if vms['code'] != OK_CODE:
102 | raise Exception('vms.code = `' + vms['code'] + '` != ' + OK_CODE)
103 | o = []
104 | ctl = vms['data']['ctl']
105 | for bid in ctl['vip']['bids']:
106 | vid = ctl['configs'][str(bid)]['vid']
107 | vms_url = Mts.vms_url(tvid, vid)
108 | print('D: bid = ' + str(bid) + ', vid = ' + str(vid))
109 | v = dl_json(vms_url)
110 | if v['code'] != OK_CODE:
111 | raise Exception('vms.code = `' + v['code'] + '` != ' + OK_CODE)
112 | m3u8_url = v['data']['m3u']
113 | o.append({
114 | 'bid': bid,
115 | 'm3u8_url': m3u8_url,
116 | })
117 | for s in vms['data']['vidl']:
118 | o.append({
119 | 'bid': s['vd'],
120 | 'm3u8_url': s['m3u'],
121 | })
122 | return o
123 |
124 | @staticmethod
125 | def output(v):
126 | o = []
127 | for i in v:
128 | one = _BID_TO_FORMAT[i['bid']].copy()
129 | one['url'] = i['m3u8_url']
130 | o.append(one)
131 | o.sort(key = lambda x: x['index'], reverse=True)
132 | return o
133 |
134 | @staticmethod
135 | def parse(html, url):
136 | info = Mts.get_info(html, url)
137 | print('D: tvid = ' + info['tvid'] + ', vid = ' + info['vid'] + ', title = ' + info['title'])
138 | vms_url = Mts.vms_url(info['tvid'], info['vid'])
139 | vms = dl_json(vms_url)
140 |
141 | v = Mts.parse_vms(vms, info['tvid'])
142 | o = {
143 | 'title': info['title'],
144 | 'v': Mts.output(v),
145 | }
146 | return o
147 |
148 | def _make_out_file(title, v):
149 | o = []
150 | o += ['# ' + title, '']
151 | for i in v:
152 | o += ['# ' + i['name'], i['url'], '']
153 | return ('\n').join(o)
154 |
155 | def main(args):
156 | OUT_FILE, RAW_FILE, URL = sys.argv[1], sys.argv[2], sys.argv[3]
157 |
158 | with open(RAW_FILE, 'rt') as f:
159 | html = f.read()
160 | o = Mts.parse(html, URL)
161 | text = _make_out_file(o['title'], o['v'])
162 |
163 | with open(OUT_FILE + '.format', 'wt') as f:
164 | f.write(text)
165 |
166 | if __name__ == '__main__':
167 | exit(main(sys.argv[1:]))
168 | # end pp_271_m_ts.py
169 |
--------------------------------------------------------------------------------
/pre_processor/pp_271-m-ts/pre_processor.meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "pp_version": "0.1.0",
3 |
4 | "id": "pp_271-m-ts",
5 | "name": "WWQ 271 移动端 m3u8",
6 | "version": "0.1.0 test20170610 0210",
7 |
8 | "type": "python3",
9 | "main": "pp_271_m_ts.py",
10 |
11 | "license": "GNU GPL v3+",
12 | "author": "sceext , wwqgtxx ",
13 | "home": "https://github.com/nilaoda/m3u8_dl-js_GUI",
14 |
15 | "_support_site": "271",
16 | "_upstream": "https://github.com/wwqgtxx/wwqLyParse"
17 | }
18 |
--------------------------------------------------------------------------------
/pre_processor/pp_le-decode/pp_le_decode.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3.6
2 | # pp_le_decode.py, m3u8_dl-js_GUI/pre_processor/pp_le-decode/
3 | #
4 | # upstream:
5 | #
6 | import sys
7 |
8 | def decode(data):
9 | version = data[0:5]
10 | if version.lower() == b'vc_01':
11 | loc2 = data[5:]
12 | length = len(loc2)
13 | loc4 = [0] * (2 * length)
14 | for i in range(length):
15 | loc4[2 * i] = int(loc2[i]) >> 4
16 | loc4[2 * i + 1] = int(loc2[i]) & 15
17 | loc6 = loc4[len(loc4) - 11 :] + loc4[:len(loc4) - 11]
18 | loc7 = [0] * length
19 | for i in range(length):
20 | loc7[i] = (loc6[2 * i] << 4) + loc6[2 * i + 1]
21 | return ('').join([chr(i) for i in loc7])
22 | else:
23 | return data
24 |
25 | def main(arg):
26 | OUT_FILE, RAW_FILE = arg[0], arg[1]
27 |
28 | with open(RAW_FILE, 'rb') as f:
29 | data = f.read()
30 | text = decode(data)
31 |
32 | with open(OUT_FILE + '.m3u8', 'wt') as f:
33 | f.write(text)
34 |
35 | if __name__ == '__main__':
36 | exit(main(sys.argv[1:]))
37 | # end pp_le_decode.py
38 |
--------------------------------------------------------------------------------
/pre_processor/pp_le-decode/pre_processor.meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "pp_version": "0.1.0",
3 |
4 | "id": "pp_le-decode",
5 | "name": "乐视 m3u8 解码",
6 | "version": "0.1.0 test20170611 1629",
7 |
8 | "type": "python3",
9 | "main": "pp_le_decode.py",
10 |
11 | "license": "GNU GPL v3+",
12 | "author": "sceext ",
13 | "home": "https://github.com/nilaoda/m3u8_dl-js_GUI",
14 |
15 | "_support_site": "www.le.com",
16 | "_upstream": "https://github.com/zhangn1985/ykdl"
17 | }
18 |
--------------------------------------------------------------------------------
/pre_processor/pp_tv-line-me/pp_tv-line-me.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3.6
2 | # pp_tv-line-me.py, m3u8_dl-js_GUI/pre_processor/pp_tv-line-me/
3 | # m3u8 pre-processor for tv.line.me
4 | #
5 | #
6 | #
7 | import sys
8 |
9 | OUT_FILE, RAW_FILE, URL = sys.argv[1], sys.argv[2], sys.argv[3]
10 |
11 |
12 | query = URL.split('?', 1)[1]
13 | base_url = URL.split('?', 1)[0].rsplit('/', 1)[0]
14 |
15 | with open(RAW_FILE, 'rt') as f:
16 | line = f.readlines()
17 |
18 | out = []
19 | for l in line:
20 | l = l.strip()
21 | if l.startswith('#') or (l == ''):
22 | out.append(l)
23 | else:
24 | out.append(base_url + '/' + l + '?' + query)
25 | out_text = ('\n').join(out) + '\n'
26 |
27 | with open(OUT_FILE + '.m3u8', 'wt') as f:
28 | f.write(out_text)
29 |
30 | # end pp_tv-line-me.py
31 |
--------------------------------------------------------------------------------
/pre_processor/pp_tv-line-me/pre_processor.meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "pp_version": "0.1.0",
3 |
4 | "id": "pp_tv-line-me",
5 | "name": "tv.line.me 的 m3u8 预处理程序",
6 | "version": "0.1.0 test20170608 1858",
7 |
8 | "type": "python3",
9 | "main": "pp_tv-line-me.py",
10 |
11 | "license": "GNU GPL v3+",
12 | "author": "sceext ",
13 | "home": "https://github.com/nilaoda/m3u8_dl-js_GUI",
14 |
15 | "_support_site": "tv.line.me"
16 | }
17 |
--------------------------------------------------------------------------------
/pre_processor/pp_wwq-any_page-url_list/pp_wwq_any_page.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3.6
2 | # pp_wwq_any_page.py, m3u8_dl-js_GUI/pre_processor/pp_wwq-any_page-url_list/
3 | #
4 | # upstream:
5 | # LICENSE: GNU GPL v3+
6 | #
7 | import sys
8 |
9 | import re
10 | from pyquery import PyQuery as pq
11 |
12 |
13 | def load_page(url):
14 | d = pq(url=url)
15 | return d
16 |
17 |
18 | class AnyPage(object):
19 |
20 | @staticmethod
21 | def check_a(raw):
22 | a = pq(raw)
23 | title = a.attr('title')
24 | if title == None:
25 | title = a.text()
26 | url = a.attr('href')
27 | # remove bad urls
28 | if url == None:
29 | return None
30 | if (title == None) or (title.strip() == ''):
31 | return None
32 |
33 | if re.match('^(http|https|ftp)://.+\.(mp4|mkv|ts|avi)', url):
34 | url = '$-> ' + url
35 | if not re.match('(^(http|https)://.+\.(shtml|html|mp4|mkv|ts|avi))|(^(http|https)://.+/video/)', url):
36 | return None
37 | if re.search('[^\?](list|mall|about|help|shop|map|vip|faq|support|download|copyright|contract|product|tencent|upload|common|index.html|v.qq.com/u/|open.baidu.com|www.iqiyi.com/lib/s_|www.iqiyi.com/dv/|top.iqiyi.com)', url):
38 | return None
39 | if re.search('(下载|播 放|播放|投诉|评论|(\d{1,2}:\d{1,2}))', title):
40 | return None
41 |
42 | return { 'title': title, 'url': url }
43 |
44 | @staticmethod
45 | def parse(html, url):
46 | d = pq(html)
47 | page_title = d('title').text()
48 |
49 | used_urls = {}
50 | o = []
51 | for i in d('a'):
52 | one = AnyPage.check_a(i)
53 | if one == None:
54 | continue
55 | # ignore dup urls
56 | if one['url'] in used_urls:
57 | continue
58 | used_urls[one['url']] = True
59 | o.append(one)
60 | return { 'title': page_title, 'list': o }
61 |
62 | def _make_out_file(raw):
63 | o = []
64 | o += ['# ' + raw['title'], '']
65 | for i in raw['list']:
66 | o += ['# ' + i['title'], i['url'], '']
67 | return ('\n').join(o)
68 |
69 | def main(arg):
70 | OUT_FILE, RAW_FILE, URL = arg[0], arg[1], arg[2]
71 |
72 | with open(RAW_FILE, 'rt') as f:
73 | html = f.read()
74 | o = AnyPage.parse(html, URL)
75 | text = _make_out_file(o)
76 |
77 | with open(OUT_FILE + '.list', 'wt') as f:
78 | f.write(text)
79 |
80 | if __name__ == '__main__':
81 | exit(main(sys.argv[1:]))
82 | # end pp_wwq_any_page.py
83 |
--------------------------------------------------------------------------------
/pre_processor/pp_wwq-any_page-url_list/pre_processor.meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "pp_version": "0.1.0",
3 |
4 | "id": "pp_wwq-any_page-url_list",
5 | "name": "WWQ 任意页面 URL 列表",
6 | "version": "0.1.0 test20170610 1616",
7 |
8 | "type": "python3",
9 | "main": "pp_wwq_any_page.py",
10 |
11 | "license": "GNU GPL v3+",
12 | "author": "sceext , wwqgtxx ",
13 | "home": "https://github.com/nilaoda/m3u8_dl-js_GUI",
14 |
15 | "_support_site": "*",
16 | "_upstream": "https://github.com/wwqgtxx/wwqLyParse",
17 | "_deps": {
18 | "pyquery": "https://pypi.python.org/pypi/pyquery",
19 | "cssselect": "https://pypi.python.org/pypi/cssselect",
20 | "lxml": "https://pypi.python.org/pypi/lxml"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/pre_processor/ppp_unpacker/ppp_unpacker.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3.6
2 | # ppp_unpacker.py, m3u8_dl-js_GUI/pre_processor/ppp_unpacker/
3 | # unpack pre-processor's `.zip.ppp` file, and check `pre_processor.meta.json`
4 | #
5 | # Usage:
6 | # --to TMP_DIR ZIP_FILE
7 | # or
8 | # --check PP_DIR
9 | #
10 | import os, sys
11 | import zipfile
12 | import shutil
13 | import json
14 |
15 |
16 | PP_VERSION = '0.1.0' # pre_processor specification version
17 | META_FILE = 'pre_processor.meta.json'
18 |
19 | # check `pre_processor.meta.json`, return True if error
20 | def _check_meta(raw_text):
21 | try:
22 | meta = json.loads(raw_text)
23 | except Exception:
24 | print('ERROR: meta: bad json format !')
25 | raise
26 | if not isinstance(meta, dict):
27 | print('ERROR: meta: bad json format !')
28 | return True
29 | must_keys = [
30 | 'pp_version',
31 | 'id', 'name', 'version',
32 | 'type', 'main',
33 | ]
34 | optional_keys = [
35 | 'license', 'author', 'copyright', 'home',
36 | ]
37 | # check pp_version
38 | if meta.get('pp_version', None) != PP_VERSION:
39 | print('ERROR: meta: pp_version != ' + PP_VERSION)
40 | return 1
41 | # check must keys
42 | bad_key = False
43 | for i in must_keys:
44 | k = meta.get(i, None)
45 | if not isinstance(k, str):
46 | print('ERROR: meta: bad key `' + i + '`')
47 | bad_key = True
48 | elif k.strip() == '':
49 | print('ERROR: meta: empty key `' + i + '`')
50 | bad_key = True
51 | # check optional keys
52 | for i in optional_keys:
53 | if (i in meta) and (not isinstance(meta[i], str)):
54 | print('ERROR: meta: bad key `' + i + '`')
55 | bad_key = True
56 | if bad_key:
57 | return 1
58 | # check unknow keys
59 | allow_keys = must_keys + optional_keys
60 | for i in meta.keys():
61 | if (not i in allow_keys) and (not i.startswith('_')):
62 | print('WARNING: unknow key `' + i + '`')
63 | # check done
64 | return False
65 |
66 |
67 | # to process `.zip.ppp` file
68 | class Ppp(object):
69 | def __init__(self, zip_file):
70 | # open the zip file
71 | self._z = zipfile.ZipFile(zip_file)
72 |
73 | def get_file_list(self):
74 | return self._z.namelist()
75 |
76 | def get_zip_root(self):
77 | for i in self.get_file_list():
78 | p = os.path.split(i)
79 | if p[0] == '':
80 | return p[1]
81 | elif p[1] == '':
82 | return p[0]
83 | return None
84 |
85 | def find_meta(self):
86 | l = self.get_file_list()
87 | if META_FILE in l:
88 | return META_FILE
89 | # find meta in root
90 | root = self.get_zip_root()
91 | if root == None:
92 | return None
93 | for i in l:
94 | p = os.path.split(i)
95 | if (p[0] == root) and (p[1] == META_FILE):
96 | return i
97 | return None
98 |
99 | def extract_meta(self):
100 | meta_file = self.find_meta()
101 | if meta_file == None:
102 | return None
103 | else:
104 | with self._z.open(meta_file, 'r') as f:
105 | return f.read()
106 |
107 | def extract(self, pp_dir):
108 | tmp_dir = pp_dir + '.tmp'
109 | self._z.extractall(tmp_dir)
110 | # move files to pp_dir
111 | root = self.get_zip_root()
112 | if root == None:
113 | os.rename(tmp_dir, pp_dir)
114 | else:
115 | os.rename(os.path.join(tmp_dir, root), pp_dir)
116 | # remove tmp_dir
117 | shutil.rmtree(tmp_dir)
118 |
119 | def _to(pp_dir, zip_file):
120 | # DEBUG
121 | print('D: open ' + zip_file)
122 | try:
123 | p = Ppp(zip_file)
124 | except Exception:
125 | print('ERROR: can not open zip file ' + zip_file)
126 | raise
127 | # find meta file
128 | meta_file = p.find_meta()
129 | if meta_file == None:
130 | print('ERROR: can not find meta file `' + META_FILE + '` !')
131 | return 1
132 | # check meta file
133 | meta_text = p.extract_meta().decode('utf-8')
134 | print('D: check meta file ' + meta_file)
135 | if _check_meta(meta_text):
136 | print('ERROR: bad meta file !')
137 | return 1
138 | # do extract
139 | print('D: extract files to ' + pp_dir)
140 | p.extract(pp_dir)
141 |
142 | return 0 # done
143 |
144 | def _check(pp_dir):
145 | meta_file = os.path.join(pp_dir, META_FILE)
146 | try:
147 | with open(meta_file, 'rb') as f:
148 | meta_text = f.read().decode('utf-8')
149 | except Exception:
150 | print('ERROR: can not open meta file ' + meta_file)
151 | raise
152 | # check meta file
153 | if _check_meta(meta_text):
154 | print('ERROR: bad meta file !')
155 | return 1
156 | return 0 # done
157 |
158 |
159 | def _p_arg(args):
160 | _a = {
161 | 'rest': args
162 | }
163 | o = {}
164 | def _next():
165 | o = _a['rest'][0]
166 | _a['rest'] = _a['rest'][1:]
167 | return o
168 | while len(_a['rest']) > 0:
169 | one = _next()
170 | if one == '--to':
171 | o['type'] = 'to'
172 | o['pp_dir'] = _next()
173 | elif one == '--check':
174 | o['type'] = 'check'
175 | o['pp_dir'] = _next()
176 | else: # default: ZIP_FILE
177 | o['zip_file'] = one
178 | return o
179 |
180 | def main(args):
181 | a = _p_arg(args)
182 | if not 'type' in a:
183 | print('ERROR: bad command line. ')
184 | return 1
185 | if a['type'] == 'to':
186 | return _to(a['pp_dir'], a['zip_file'])
187 | else:
188 | return _check(a['pp_dir'])
189 |
190 | if __name__ == '__main__':
191 | exit(main(sys.argv[1:]))
192 |
193 | # end ppp_unpacker.py
194 |
--------------------------------------------------------------------------------