(SOFT) SOFT (2012 год)

Опыт видео-конвертации

Видеохостингом называется портал, который хранит у себя не просто ссылки на чужие ролики, а собственно мультимедиа-файлы - как это делает http://rutube.ru/ или http://www.youtube.com. Свои собственные видеохостинги я делал с незапамятных времен. В 2007 году я впервые сделал видеохостинг с упором на разлекламированную тогда технологих AJAX (Мультимедиа на Web-страничках) - этот опыт оказался неудачным и научил меня, что у микрософта не 1% технологий достаточно приемлимые для практического применения, а гораздо меньше. Более поздние мои решения по видеохостингу оказались более удачными - самое удачное решение http://video.votpusk.ru/.

Надо сказать, что в природе существуют некие совершенно убогие видео-порталы, которые могут принимать загружаемые пользователями ролики только в одном формате, чаще всего FLV. Я изначально подходил к этому вопросу только так, чтобы принимать ролики произвольных форматов и чтобы мой портал сам выполнял конвертацию в нужный мне формат. Об этом аспекте построения видеохостингов я и собираюсь рассказать на этой страничке.

В 2009-м году я описал кое-какие свои мысли по построению видеохостингов (OpenSource-проекты для работы с видео и торрентами), однако с той поры много воды утекло и стало ясно, что бороться на них надо над теми проблемами, которых не было видно 2-3 года назад.

В качестве примера такой проблемы, которая возникает на практике и совсем не видна начинающему строителю видеохостингов - кратковременные пропадания сети в локалке (по загадочной причине), которые приводят к полной неработоспособности видеохостинга. Ведь задача конвертации видео должна размещаться на отдельной виртуалке (или отдельной физической машине, связанной с Web-порталом по локалке. Выполнять конвертации на одном кампутере с Web-порталом совершенно немыслимо - портал будет парализован задачей видео-конвертации (которая поставит процессор в 100% например на целый час.

Поэтому в результате практического опыта работы с видеоконвертерами у меня в коде моего конвертеров вокруг всех запросов в базу появилась вот такая обвязка ReConnect - которая не дает упасть конвертеру при проблемах в сети или недоступности SQL:

  203:          ...
  204:      Sub ReConnect()
  205:          Dim ReconnectCounter2 As Integer
  206:  Start2:
  207:          Try
  208:              If Browse_CN Is Nothing Then Browse_CN = New System.Data.SqlClient.SqlConnection(My.Settings.SQLServer_ConnectionStrings)
  209:              If Browse_CN.State <> ConnectionState.Open Then Browse_CN.Open()
  210:              If Browse_CMD Is Nothing Then Browse_CMD = New System.Data.SqlClient.SqlCommand(My.Settings.Browse_Query, Browse_CN)
  211:          Catch ex As System.Data.SqlClient.SqlException
  212:              ReconnectCounter2 += 1
  213:              If ReconnectCounter2 > ConverterControlParameters.Rows(0)("MaxReconnect") Then
  214:                  Throw New Exception("Нет коннекта к базе " & vbCrLf & ex.Message)
  215:              Else
  216:                  Threading.Thread.Sleep(CInt(ConverterControlParameters.Rows(0)("SurveyTime")) * 1000)
  217:                  GoTo Start2
  218:              End If
  219:   
  220:          End Try
  221:      End Sub
  222:          ...

Подобных проблем накопилось за годы эксплуатации видеоконвертации много. Например поначалу я строил сами конвертеры с рабочим столом, запуская процесс видеоконвертации в BackgroundWorker. Однако, проходила неделя-другая и видеоковертер зависал. Я стал экспериментировать по-разному. В конце-концов когда я убрал BackgroundWorker и многократно все перепроверил - стало понятно что налицо очередное микрософтовское свинство. Где-то идет утечка памяти в BackgroundWorker и проги с ним не могут работать больше нескольких недель. Хотя у меня есть одна прога, которая работает обычно примерно месяц, но она не манипулирует потоками - WebDownloader_UltraLite - ваш личный поисковик по рунету с особыми возможностями поиска - и в этом случае BackgroundWorker работает нормально. Но при активной манипуляции потоками в BackgroundWorker - начинаются утечки памяти и больше двух недель прога не работает. Со временем изменился и сам комплект кодеков конвертации.

Вот так, собственно, у меня и накопился опыт построения видео-хостингов.


На этой страничке я не буду описывать свой видеоконвертер полностью в виде OpenSource, ибо это коммерческая разработка, я покажу лишь общий принцип его работы:


   1:       Sub Survey()
   2:          .....
   3:          While True
   4:                  ....
   5:                  OneConverting.GoCoder(Survey_RDR("i"), CInt(ConverterControlParameters.Rows(0)("SurveyTime")) * 1000, ConverterControlParameters.Rows(0)("Parameters"), ConverterControlParameters.Rows(0)("MaxReconnect"), CInt(ConverterControlParameters.Rows(0)("Timeout")) * 1000)
   6:                   .....
   7:              'ждем
   8:              Threading.Thread.Sleep(CInt(ConverterControlParameters.Rows(0)("SurveyTime")) * 1000)
   9:          End While
  10:          .....
  11:      End Sub
  12:   
  13:  Class OneConverting
  14:      Shared Sub GoCoder(ByVal i As Integer, ByVal SurveyTime As Integer, ByVal ConvertOptions As String, ByVal MaxReconnect As Integer, ByVal Timeout As Integer)
  15:                  ....
  16:                  'запускаем собственно конвертацию
  17:                  Dim MediaCoder As New System.Diagnostics.Process
  18:                  MediaCoder.StartInfo.Arguments = " -i " & SharedSourceFileName & " " & ConvertOptions & " " & SharedTargetFileName '& " 2>""" & ErrLogFileName & """"
  19:                  MediaCoder.StartInfo.FileName = System.IO.Path.Combine(My.Settings.MediaCoder_Directory, "ffmpeg.exe")
  20:                  MediaCoder.StartInfo.WorkingDirectory = My.Settings.FLVFolder_ShareDiskName
  21:                  MediaCoder.StartInfo.UseShellExecute = False
  22:                  MediaCoder.StartInfo.RedirectStandardError = True
  23:                  MediaCoder.Start()
  24:                  MediaCoder.WaitForExit(Timeout)
  25:                  OutputErrorStream = MediaCoder.StandardError
  26:                  ErrText = OutputErrorStream.ReadToEnd
  27:                  OutputErrorStream.Close()
  28:                  MediaCoder.Close()
  29:                  WriteError(i, ErrText)
  30:                  ...
  31:      End Sub
  32:  End Class

Продолжая свой курс на поддержку OpenSource-программирования - я стараюсь описать небольшие части даже моих закрытых коммерческих разработок. Чтобы в интернете присутствовали хотя бы некоторые реальные, пусть даже небольшие, фрагменты кода реальных коммерческих разработок. Хотя я не буду описывать здесь весь конвертер или всю его админку, я тем опишу несколько компонентов админки моего видеоконвертера, носящих универсальный характер.

Только не путайте пользовательский интерфейс видеоконвертера (это интерфейс администратора видеохостинга) с тем что видит конечный пользователь, загружающий свои ролики на сайт. Что именно видит конечный пользователь - вы можете увидеть, зарегистрировавшись на любом моем видеохостинге, например video.votpusk.ru и добавив там свой ролик в произвольном видео-формате. А здесь я опишу то, что видит только администратор портала votpusk.ru в части управления моим видеоконвертером.

В других порталах я разбивал функционал управления видеоконвертером по-другому по пунктам меню - можно вообще все свести в один пункт, но для определенности остановимся на вот такой (более подробной разбивке функционала управления видеоконвертером).

На этом рисунке вы видите фрагмент моей админки портала votpusk.ru - зеленым тут отчеркнуты пять пунктов, что относится к видеоконвертеру:




Пункт "сконвертированы" это просто запросы в базу отражающие нормально сконвертированные ролики. Здесь администратор может поставить ролик на титульную страницу, как лучший ролик сайта или забанить его вообще. Пункт "конвертируются" отображает очереди на конвертацию. Пункт "несконвертированы" - это неудачно сконвертированные ролики с сообщением об ошибке. Здесь администратор может только удалить запись в базе о неудачном конвертировании ролика. Пункт видеофайлы - позволяет манипулировать отдельными файлами видеоконвертера - а не записыми в базе.

Как вы поняли, в этом моем решении видеофайлы хранятся не в базе, как например я описывал здесь (Cекционирование графики при SQL-хранении) - поэтому есть отдельно операции над записыми в базе и операции над различными файлами видеоконвертера.

Прежде всего посмотрим на фрейм - "управление процессом VideoConverter". Как вы понимаете, это именно фрейм - ибо расположен он на физической машине с видео-конвертером (в отличии от собственно Web-портала и админки Web-портала, которые размещены совсем на других кампутерах).

Это автономный проектик, состоящий из сайтика на две странички - login.aspx и Defaul.aspx. Обратите внимание, что этот сайт должен запускаться под учетной записью LocalSystem - иначе доступа к перечню процессов машины у него не будет.

Страничка Default.aspx Устроена так:


   1:  <%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %>
   2:   
   3:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   4:   
   5:  <html xmlns="http://www.w3.org/1999/xhtml">
   6:  <head id="Head1" runat="server">
   7:      <title>Управление процессом <asp:Literal ID="Literal1" runat="server" Text="<%$ appSettings:ProcessControlName %>"/></title>
   8:  </head>
   9:  <body>
  10:      <form id="form1" runat="server">
  11:      <div>
  12:      <h4>Управление процессом 
  13:          <asp:Label ID="Label3" runat="server" Text='<%$ appSettings:ProcessControlName %>'></asp:Label>
  14:      </h4>
  15:      
  16:          <asp:LinkButton ID="LinkButton4" runat="server">Start</asp:LinkButton>
  17:          <asp:DataList ID="DataList1" runat="server" CellPadding="2" >
  18:              <ItemTemplate>
  19:                  <asp:LinkButton ID="LinkButton1" runat="server" onclick="LinkButton1_Click" CommandArgument='<%# Eval("ID") %>'>Restart</asp:LinkButton>
  20:                  </td><td>
  21:                  <asp:LinkButton ID="LinkButton3" runat="server" onclick="LinkButton3_Click" CommandArgument='<%# Eval("ID") %>'>Kill</asp:LinkButton>
  22:                  </td><td>
  23:                  <asp:Label ID="Label1" runat="server" Text='<%# Eval("ID") %>' ></asp:Label>
  24:                  </td><td>
  25:                  <asp:Label ID="Label2" runat="server" Text='<%# Eval("StartTime") %>' ></asp:Label>
  26:              </ItemTemplate>
  27:          </asp:DataList>
  28:          <asp:LinkButton ID="LinkButton2" runat="server">Refresh</asp:LinkButton>
  29:          <br /><asp:Label ID="lErr1" runat="server" ForeColor="Red"></asp:Label>
  30:      </div>
  31:      </form>
  32:  </body>
  33:  </html>
   1:   
   2:  Partial Class _Default
   3:      Inherits System.Web.UI.Page
   4:   
   5:      Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   6:          If Not IsPostBack Then
   7:              If Not Me.Page.User.Identity.IsAuthenticated Then
   8:                  Response.Redirect("Login.aspx")
   9:                  Exit Sub
  10:              End If
  11:              If Not Roles.IsUserInRole(Me.Page.User.Identity.Name, "UserAdmin") Then
  12:                  Exit Sub
  13:              End If
  14:          End If
  15:          If Not IsPostBack Then
  16:              Refresh()
  17:          End If
  18:      End Sub
  19:   
  20:      Sub Refresh()
  21:          Dim X() As System.Diagnostics.Process = System.Diagnostics.Process.GetProcessesByName(System.Configuration.ConfigurationManager.AppSettings("ProcessControlName"))
  22:          DataList1.DataSource = X
  23:          DataList1.DataBind()
  24:      End Sub
  25:   
  26:   
  27:      Protected Sub LinkButton2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles LinkButton2.Click
  28:          Refresh()
  29:      End Sub
  30:   
  31:      Protected Sub LinkButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
  32:          lErr1.Text = ""
  33:          Dim LinkButton1 As LinkButton = CType(sender, LinkButton)
  34:          Dim PID As Integer = LinkButton1.CommandArgument
  35:          Try
  36:              Dim X As System.Diagnostics.Process = System.Diagnostics.Process.GetProcessById(PID)
  37:              X.Kill()
  38:              System.Diagnostics.Process.Start(System.Configuration.ConfigurationManager.AppSettings("ProcessStart"))
  39:          Catch ex As Exception
  40:              lErr1.Text = ex.Message
  41:          End Try
  42:          Refresh()
  43:      End Sub
  44:   
  45:   
  46:      Protected Sub LinkButton3_Click(ByVal sender As Object, ByVal e As System.EventArgs)
  47:          lErr1.Text = ""
  48:          Dim LinkButton1 As LinkButton = CType(sender, LinkButton)
  49:          Dim PID As Integer = LinkButton1.CommandArgument
  50:          Try
  51:              Dim X As System.Diagnostics.Process = System.Diagnostics.Process.GetProcessById(PID)
  52:              X.Kill()
  53:          Catch ex As Exception
  54:              lErr1.Text = ex.Message
  55:          End Try
  56:          Refresh()
  57:      End Sub
  58:   
  59:      Protected Sub LinkButton4_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles LinkButton4.Click
  60:          lErr1.Text = ""
  61:          Try
  62:              System.Diagnostics.Process.Start(System.Configuration.ConfigurationManager.AppSettings("ProcessStart"))
  63:          Catch ex As Exception
  64:              lErr1.Text = ex.Message
  65:          End Try
  66:          Refresh()
  67:      End Sub
  68:  End Class

Страничка Login.aspx совсем простая:


   1:  <%@ Page Language="VB"  AutoEventWireup="false" CodeFile="Login.aspx.vb" Inherits="Login"  %>
   2:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   3:   
   4:  <html xmlns="http://www.w3.org/1999/xhtml" >
   5:  <body >
   6:      <form id="form1" runat="server">
   7:      <div>
   8:        <asp:Login ID="Login1" runat="server" LoginButtonText="Войти" PasswordLabelText="Пароль:"
   9:          RememberMeText="Помнить меня" TitleText="Вход" UserNameLabelText="EMail:" DestinationPageUrl="Default.aspx" PasswordRecoveryText="Забыл пароль" PasswordRecoveryUrl="#">
  10:          <TitleTextStyle Font-Size="Small" />
  11:          <CheckBoxStyle Font-Size="Small" />
  12:          <LabelStyle Font-Size="Small" />
  13:          <HyperLinkStyle Font-Size="Small" />
  14:          <InstructionTextStyle Font-Size="Small" />
  15:          <TextBoxStyle Font-Size="Small" />
  16:          <ValidatorTextStyle Font-Size="Small" />
  17:          <FailureTextStyle Font-Size="Small" />
  18:      </asp:Login>
  19:      </div>
  20:      </form>
  21:  </body>
  22:  </html>
   1:  Partial Class login
   2:      Inherits System.Web.UI.Page
   3:   
   4:      Protected Sub Login1_Authenticate(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.AuthenticateEventArgs) Handles Login1.Authenticate
   5:          Login1.PasswordRecoveryUrl = "http://user.votpusk.ru/RestorePass.aspx"
   6:          If Membership.ValidateUser(Login1.UserName, Login1.Password) Then
   7:              e.Authenticated = True
   8:              If Login1.RememberMeSet Then
   9:                  Dim ticket As New FormsAuthenticationTicket(Login1.UserName, True, 5000)
  10:                  Dim EncryptTicket As String = FormsAuthentication.Encrypt(ticket)
  11:                  Dim AUCook As HttpCookie = New HttpCookie(FormsAuthentication.FormsCookieName, EncryptTicket)
  12:                  AUCook.Domain = "votpusk.ru"
  13:                  Response.Cookies.Add(AUCook)
  14:              Else
  15:                  FormsAuthentication.SetAuthCookie(Login1.UserName, False)
  16:              End If
  17:              Login1.DestinationPageUrl = "Default.aspx"
  18:          Else
  19:              Login1.DestinationPageUrl = "Login.aspx"
  20:              e.Authenticated = False
  21:          End If
  22:          '
  23:          'признак аутентификации для клиентского скрипта
  24:          Dim AU_Cook As New HttpCookie("AU")
  25:          AU_Cook.Item("U") = 1
  26:          Response.Cookies.Set(AU_Cook)
  27:          ''
  28:      End Sub
  29:   
  30:      Protected Sub Login1_LoginError(ByVal sender As Object, ByVal e As System.EventArgs) Handles Login1.LoginError
  31:          FormsAuthentication.SignOut()
  32:          'признак аутентификации для клиентского скрипта
  33:          Dim AU_Cook As New HttpCookie("AU")
  34:          AU_Cook.Item("U") = 0
  35:          Response.Cookies.Set(AU_Cook)
  36:      End Sub
  37:   
  38:  End Class

Для работы странички Login надо правильно сконфигурить Membership-провайдеры. Делается это так:


   1:  <?xml version="1.0"?>
   2:  <!-- 
   3:      Note: As an alternative to hand editing this file you can use the 
   4:      web admin tool to configure settings for your application. Use
   5:      the Website->Asp.Net Configuration option in Visual Studio.
   6:      A full list of settings and comments can be found in 
   7:      machine.config.comments usually located in 
   8:      \Windows\Microsoft.Net\Framework\v2.x\Config 
   9:  -->
  10:  <configuration>
  11:      <appSettings>
  12:          <add key="ProcessControlName" value="VideoConverter"/>
  13:          <add key="ProcessStart" value="J:\VideoConvert\VideoConverter.exe"/>
  14:      </appSettings>
  15:      <connectionStrings>
  16:          <!-- connectionStrings нужен тут только для аутентификации -->
  17:          <remove name="LocalSqlServer"/>
  18:          <add name="LocalSqlServer" connectionString="server=XXXXXX;Initial Catalog=YYYYYYYY;Max Pool Size=1000;User ID=VotpuskLogin;Password=ZZZZZZZZZ" providerName="System.Data.SqlClient"/>
  19:      </connectionStrings>
  20:      <system.web>
  21:          <!-- секции authentication,roleManager,membership нужны тут только для аутентификации -->
  22:          <authentication mode="Forms"/>
  23:          <roleManager enabled="true" defaultProvider="AspNetSqlProvider">
  24:              <providers>
  25:                  <remove name="AspNetSqlRoleProvider"/>
  26:                  <remove name="AspNetWindowsTokenRoleProvider"/>
  27:                  <add applicationName="/Votpusk" name="AspNetSqlProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="LocalSqlServer"/>
  28:              </providers>
  29:          </roleManager>
  30:          <membership>
  31:              <providers>
  32:                  <remove name="AspNetSqlMembershipProvider"/>
  33:                  <add applicationName="/Votpusk" name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider,System.Web, Version=2.0.0.0, Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="LocalSqlServer" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="False" requiresUniqueEmail="True" minRequiredPasswordLength="1" minRequiredNonalphanumericCharacters="0" passwordFormat="Hashed" maxInvalidPasswordAttempts="6" passwordAttemptWindow="10" passwordStrengthRegularExpression=""/>
  34:              </providers>
  35:          </membership>
  36:          <compilation debug="true" strict="false" explicit="true">
  37:          </compilation>
  38:          <pages>
  39:              <namespaces>
  40:                  <clear/>
  41:                  <add namespace="System"/>
  42:                  <add namespace="System.Collections"/>
  43:                  <add namespace="System.Collections.Generic"/>
  44:                  <add namespace="System.Collections.Specialized"/>
  45:                  <add namespace="System.Configuration"/>
  46:                  <add namespace="System.Text"/>
  47:                  <add namespace="System.Text.RegularExpressions"/>
  48:                  <add namespace="System.Linq"/>
  49:                  <add namespace="System.Xml.Linq"/>
  50:                  <add namespace="System.Web"/>
  51:                  <add namespace="System.Web.Caching"/>
  52:                  <add namespace="System.Web.SessionState"/>
  53:                  <add namespace="System.Web.Security"/>
  54:                  <add namespace="System.Web.Profile"/>
  55:                  <add namespace="System.Web.UI"/>
  56:                  <add namespace="System.Web.UI.WebControls"/>
  57:                  <add namespace="System.Web.UI.WebControls.WebParts"/>
  58:                  <add namespace="System.Web.UI.HtmlControls"/>
  59:              </namespaces>
  60:          </pages>
  61:      </system.web>
  62:      <system.codedom/>
  63:      <system.webServer/>
  64:  </configuration>

Теперь посмотрим как устроен более интересный компонент - синхронизатор, который необходим для всех сайтов где файлы физически хранятся в файловой системе, а их учет обеспевивается в базе. Понятно, что в силу тех или иных причин со временем учет в базе расходится с фактически хранящимися на дискке файлами. И в учете в базе появляются файлы, которых фактически нет на диске (и юзеру предлагается посмотреть видеоролик, но фактически его нет). Либо наоборот - на диске есть неучтенные видеофайлы, которые занимают место, но в учете их нет и юзер их посмотреть не может.




Этот синхронизатор выполнен мною в виде одной странички, которая позволяет рекурсивно обойти весь каталог с видеофайлами (их четыре вида, как вы видите: Upload - загружаемые пользователями файлы, Log - протоколы распознавания формата загружаемых файлов, Jpg - preview пользовательсткого ролика, Flv - сконвертированные видеоролики, пригодные к воспроизведению (на самом деле есть и пятый вид - еще один журнал виделконвертации, который вы видите на всплывающей форме неудачных конвертаций - он хранится в базе). Обнаружив все видеофайлы - синхронизатор на форме показывает - какие файлы не учтены в базе и позволяет их удалить (красные кнопки).

Итак посмотрим как устроен этот синхронизатор (пункт админки видеофайлы). С точки зрения формы - это просто табличка:


   1:  <%@ Page Language="VB" AutoEventWireup="false" CodeFile="AdminVideoFiles.aspx.vb" Inherits="AdminVideoFiles" %>
   2:   
   3:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   4:   
   5:  <html xmlns="http://www.w3.org/1999/xhtml">
   6:  <head runat="server">
   7:  </head>
   8:  <body>
   9:      <form id="form1" runat="server">
  10:      <div>
  11:          <asp:DataList ID="DataList1" runat="server">
  12:              <ItemTemplate>
  13:                  <asp:ImageButton ID="DelButton1" ToolTip="Удалить файл" runat="server" 
  14:                      ImageUrl="~/Images/ico0013.gif" onclick="DelButton1_Click" />
  15:                  </td><td>
  16:                  <asp:HyperLink ID="FileLink1" runat="server" Target="_blank">HyperLink</asp:HyperLink>
  17:              </ItemTemplate>
  18:          </asp:DataList>
  19:      </div>
  20:      <asp:Label ID="Lerr1" runat="server" ForeColor="Red"></asp:Label>
  21:      </form>
  22:  </body>
  23:  </html>

Алгоритм у этой формы - рекурсивный обход каталога. Можно было бы сделать это на любых структурах данных, например на Дженериках , как я например сделал здесь (Практическое применение наследования, полиморфизма, интерфейсов, дженериков и делегатов на примерах в Visual Basic .NET.), но я решил эту задачку в простом классическом стиле бейсика:


   1:   
   2:  Partial Class AdminVideoFiles
   3:      Inherits System.Web.UI.Page
   4:   
   5:      Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   6:          If Not IsPostBack Then
   7:              If Not Me.Page.User.Identity.IsAuthenticated Then
   8:                  Response.Redirect("Login.aspx")
   9:                  Exit Sub
  10:              End If
  11:              If Not Roles.IsUserInRole(Me.Page.User.Identity.Name, "UserAdmin") Then
  12:                  Response.Redirect(Votpusk.[GoTo].NotSupported & "?ErrorMessage=""Эта функция доступна только администраторам.""&URL=""" & Request.RawUrl & """")
  13:              End If
  14:              If Request.QueryString("type") IsNot Nothing Then
  15:                  If Request.QueryString("type") <> "" Then
  16:                      Select Case Request.QueryString("type").ToLower
  17:                          Case "flv"
  18:                              CurrentAction = ActionType.FLV
  19:                              GetFileList(HttpContext.Current, ActionType.FLV)
  20:                          Case "log"
  21:                              CurrentAction = ActionType.LOG
  22:                              GetFileList(HttpContext.Current, ActionType.LOG)
  23:                          Case "jpg"
  24:                              CurrentAction = ActionType.JPG
  25:                              GetFileList(HttpContext.Current, ActionType.JPG)
  26:                          Case "upload"
  27:                              CurrentAction = ActionType.UPLOAD
  28:                              GetFileList(HttpContext.Current, ActionType.UPLOAD)
  29:                      End Select
  30:                  End If
  31:              End If
  32:              CurrentDataitemNamber = 0
  33:              DataList1.DataSource = FLV_DirList
  34:              DataList1.DataBind()
  35:          End If
  36:      End Sub
  37:   
  38:      Dim CurrentAction As ActionType, CurrentDataitemNamber As Integer = 0
  39:      Protected Sub DataList1_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataListItemEventArgs) Handles DataList1.ItemDataBound
  40:          If e.Item.DataItem IsNot Nothing Then
  41:              CurrentDataitemNamber += 1
  42:              Dim DelButton1 As ImageButton = CType(e.Item.FindControl("DelButton1"), ImageButton)
  43:              Dim FileLink1 As HyperLink = CType(e.Item.FindControl("FileLink1"), HyperLink)
  44:   
  45:              Dim FullFileName As String = IO.Path.Combine(SumDirList(FLV_DirList(CurrentDataitemNamber)), FLV_FileList(CurrentDataitemNamber))
  46:              Dim Pos1 As Integer = FullFileName.IndexOf("FFF")
  47:              Dim HrefLink As String = "http://video.votpusk.ru/FFF/" & FullFileName.Substring(Pos1 + 4).Replace("\", "/")
  48:              FileLink1.NavigateUrl = HrefLink
  49:              FileLink1.Text = FullFileName
  50:              If Not FileExistInDB(FLV_FileList(e.Item.DataItem), CurrentAction) Then
  51:                  DelButton1.Visible = True
  52:                  DelButton1.CommandName = FullFileName
  53:              Else
  54:                  DelButton1.Visible = False
  55:              End If
  56:          End If
  57:      End Sub
  58:   
  59:      Sub GetFileList(ByVal context As HttpContext, ByVal ListActionType As ActionType)
  60:          Dim LocalVideoDirectory As String = context.Server.MapPath("FFF")
  61:          FLV_FileList = New Collection
  62:          FLV_DirList = New Collection
  63:          SumDirList = New Collection
  64:          Select Case ListActionType
  65:              Case ActionType.FLV : ProcessDir(LocalVideoDirectory, "*.flv")
  66:              Case ActionType.LOG : ProcessDir(LocalVideoDirectory, "*.log")
  67:              Case ActionType.JPG : ProcessDir(LocalVideoDirectory, "*.jpg")
  68:              Case ActionType.UPLOAD : ProcessDir(LocalVideoDirectory, "*.upload")
  69:          End Select
  70:      End Sub
  71:   
  72:      Function FileExistInDB(ByVal FileName As String, ByVal CheckActionType As ActionType) As Boolean
  73:          Select Case CheckActionType
  74:              Case ActionType.FLV
  75:                  Dim CheckVideoFlvFile As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, "CheckVideoFlvFile")
  76:                  CheckVideoFlvFile.SelectCommandType = SqlDataSourceCommandType.StoredProcedure
  77:                  CheckVideoFlvFile.SelectParameters.Add("FlvFile", FileName)
  78:                  Dim DV1 As Data.DataView = CheckVideoFlvFile.Select(New DataSourceSelectArguments)
  79:                  If DV1 Is Nothing Then
  80:                      Return False
  81:                  Else
  82:                      If DV1.Count = 0 Then
  83:                          Return False
  84:                      Else
  85:                          Return True
  86:                      End If
  87:                  End If
  88:   
  89:              Case ActionType.JPG
  90:                  Dim CheckVideoJPGFile As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, "CheckVideoJpgFile")
  91:                  CheckVideoJPGFile.SelectCommandType = SqlDataSourceCommandType.StoredProcedure
  92:                  CheckVideoJPGFile.SelectParameters.Add("JpgFile", FileName)
  93:                  Dim DV1 As Data.DataView = CheckVideoJPGFile.Select(New DataSourceSelectArguments)
  94:                  If DV1 Is Nothing Then
  95:                      Return False
  96:                  Else
  97:                      If DV1.Count = 0 Then
  98:                          Return False
  99:                      Else
 100:                          Return True
 101:                      End If
 102:                  End If
 103:              Case ActionType.UPLOAD
 104:                  Dim CheckVideoUploadFile As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, "CheckVideoSourceFile")
 105:                  CheckVideoUploadFile.SelectCommandType = SqlDataSourceCommandType.StoredProcedure
 106:                  CheckVideoUploadFile.SelectParameters.Add("SourceFile", FileName)
 107:                  Dim DV1 As Data.DataView = CheckVideoUploadFile.Select(New DataSourceSelectArguments)
 108:                  If DV1 Is Nothing Then
 109:                      Return False
 110:                  Else
 111:                      If DV1.Count = 0 Then
 112:                          Return False
 113:                      Else
 114:                          Return True
 115:                      End If
 116:                  End If
 117:              Case ActionType.LOG
 118:                  'не учитываются в базе
 119:                  Return False
 120:          End Select
 121:   
 122:      End Function
 123:   
 124:      Enum ActionType
 125:          FLV = 1
 126:          LOG = 2
 127:          JPG = 3
 128:          UPLOAD = 4
 129:      End Enum
 130:   
 131:      Dim FLV_FileList As Collection
 132:      Dim FLV_DirList As Collection
 133:      Dim SumDirList As Collection
 134:   
 135:      ''' <summary>
 136:      ''' Рекурсивный обход каталогов
 137:      ''' </summary>
 138:      Private Sub ProcessDir(ByVal StartPath As String, ByVal FileExtension As String)
 139:          If StartPath.Contains(":\RECYCLER\") Or StartPath.Contains(":\System Volume Information") Then
 140:              'корзину и системные области каждого диска не чистим
 141:              Exit Sub
 142:          End If
 143:          SumDirList.Add(StartPath)
 144:          '
 145:          Dim X As New IO.DirectoryInfo(StartPath)
 146:          Dim _FLV_FileList() As IO.FileInfo
 147:          Dim DirList() As IO.DirectoryInfo
 148:          '
 149:          Try
 150:              _FLV_FileList = X.GetFiles(FileExtension)
 151:          Catch ex As Exception
 152:              'молча
 153:              Exit Sub
 154:          End Try
 155:          '
 156:          Try
 157:              For Each OneFile As IO.FileInfo In _FLV_FileList
 158:                  FLV_DirList.Add(SumDirList.Count) 'поставим ссылочку на текущую директорию
 159:                  FLV_FileList.Add(OneFile.Name)
 160:              Next
 161:          Catch ex As Exception
 162:              'молча
 163:          End Try
 164:          '
 165:          Try
 166:              DirList = X.GetDirectories()
 167:          Catch ex As Exception
 168:              'молча
 169:              Exit Sub
 170:          End Try
 171:          '
 172:          For Each OneDir As IO.DirectoryInfo In DirList
 173:              ProcessDir(OneDir.FullName, FileExtension)
 174:          Next
 175:      End Sub
 176:   
 177:   
 178:      Protected Sub DelButton1_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs)
 179:          Dim DelButton1 As ImageButton = CType(sender, ImageButton)
 180:          Dim FullFileName As String = DelButton1.CommandName
 181:          If My.Computer.FileSystem.FileExists(FullFileName) Then
 182:              Try
 183:                  My.Computer.FileSystem.DeleteFile(FullFileName)
 184:                  Lerr1.Text = ""
 185:              Catch ex As Exception
 186:                  Lerr1.Text = ex.Message
 187:              End Try
 188:          Else
 189:              Lerr1.Text = "file not found"
 190:          End If
 191:      End Sub
 192:  End Class

Если у вашей фирмы нет хорошего программиста для построения видеохостингов и видеоконвертации с нуля, вы можете заказать готовое решение у меня по реквизитам указанным на портале //www.vb-net.com/.



Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19>  <20>  <21>  <22>  <23
Link to this page: //www.vb-net.com/VideoConverter/index.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <CHAT ME>  <ABOUT ME>  < THANKS ME>